Documentation/Tools & Function Calling
v2.0.0-beta.1Snapshot bebdcd1Markdown ↗

Tools & Function Calling

Tools & Function Calling

Defining Tools

typescript
// Documentation check: complete example
import { type ToolFunction } from 'oneringai';

const weatherTool: ToolFunction = {
  definition: {
    type: 'function',
    function: {
      name: 'get_weather',
      description: 'Get current weather for a location',
      parameters: {
        type: 'object',
        properties: {
          location: {
            type: 'string',
            description: 'City name, e.g., "San Francisco"',
          },
          units: {
            type: 'string',
            enum: ['celsius', 'fahrenheit'],
            description: 'Temperature units',
          },
        },
        required: ['location'],
      },
    },
  },
  execute: async (args) => {
    // Your implementation
    const { location, units = 'fahrenheit' } = args;

    // Call weather API
    const temp = 72; // Example

    return {
      location,
      temperature: temp,
      units,
      conditions: 'sunny',
    };
  },
};

Using Tools

typescript
const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: [weatherTool, calculatorTool, searchTool],
});

const response = await agent.run('What is the weather in Paris?');

// Agent will:
// 1. Recognize it needs weather data
// 2. Call weatherTool with { location: "Paris" }
// 3. Receive result
// 4. Generate natural language response

console.log(response.output_text);
// "The current weather in Paris is 72°F and sunny."

Tool Management

typescript
const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: [tool1, tool2],
});

// Add a tool
agent.addTool(newTool);

// Remove a tool
agent.removeTool('tool_name');

// Replace all tools
agent.setTools([tool1, tool2, tool3]);

// List available tools
const toolNames = agent.listTools();
console.log(toolNames); // ['get_weather', 'calculate', 'search']

Tool Execution Context

Tools receive a context object with useful information:

typescript
const myTool: ToolFunction = {
  definition: {
    type: 'function',
    function: {
      name: 'my_tool',
      description: 'Example tool',
      parameters: {
        type: 'object',
        properties: {
          input: { type: 'string' },
        },
        required: ['input'],
      },
    },
  },
  execute: async (args, context) => {
    // Identity (auto-populated from agent config):
    console.log(context?.agentId);  // Agent identifier
    console.log(context?.userId);   // User ID (set via agent.userId or config)

    // Connector registry (scoped to agent's userId + identities):
    if (context?.connectorRegistry) {
      const names = context.connectorRegistry.list();  // Available connector names
      const gh = context.connectorRegistry.get('github'); // Get connector instance
    }

    // Working memory (when workingMemory feature is enabled):
    if (context?.memory) {
      const data = await context.memory.get('some_key');
    }

    // Cancellation:
    if (context?.signal?.aborted) {
      return { error: 'Cancelled' };
    }

    return { result: 'done' };
  },
};

Built-in Tools Overview

Tool counts are deliberately not presented as one fixed headline number: 39 connector-free tools live in the generated registry, while context plugins, configured connectors, MCP servers, and the orchestrator add tools at runtime.

Source Registration model
Generated built-ins ToolRegistry.getBuiltInTools(); stable connector-free registry
Context plugins Enabled by context.features (store_*, memory_*, catalog, TODO tools)
Connector tools Created from live connectors by ConnectorTools.for() / discoverAll()
Application and MCP tools Supplied by the host or discovered from connected MCP servers

The complete source-of-truth inventory is in the Connector & Tool Catalog.

Category Tools Description
Unified Store store_get, store_set, store_delete, store_list, store_action Generic CRUD for all plugin stores: memory, context, instructions, user_info, workspace (auto-registered)
Filesystem read_file, write_file, edit_file, glob, grep, list_directory Local file operations
Shell bash, bg_process_kill, bg_process_list, bg_process_output, dev_server Shell command execution, background process management, and dev server orchestration
Web web_fetch (built-in), web_search / web_scrape (ConnectorTools) Web content retrieval, search, and scraping
Desktop desktop_screenshot, desktop_mouse_*, desktop_keyboard_*, desktop_window_*, desktop_get_* OS-level desktop automation (11 tools, requires @nut-tree-fork/nut-js)
Code execute_javascript Sandboxed JavaScript execution
JSON json_manipulate JSON object manipulation (add, delete, replace fields)
Custom tools custom_tool_draft, custom_tool_test, custom_tool_save, custom_tool_load, custom_tool_list, custom_tool_delete Let agents create, test, persist, and reuse sandboxed tools
Routines generate_routine, routine_get/list/update/delete, task-step tools, execution listing Build and manage persisted multi-step routines (8 tools)
GitHub search_files, search_code, read_file, list_branches, get_pr, pr_files, pr_comments, create_pr GitHub API operations (8 tools, auto-registered for GitHub connectors)
Slack Channel, message, thread, search, mention, reaction, and user tools Slack API operations (10 tools, auto-registered)
Microsoft create_draft_email, send_email, create_meeting, edit_meeting, get_meeting, list_meetings, find_meeting_slots, get_meeting_transcript, read_file, list_files, search_files Microsoft Graph tools (11 tools, auto-registered)
Google create_draft_email, send_email, create_meeting, edit_meeting, get_meeting, list_meetings, find_meeting_slots, get_meeting_transcript, read_file, list_files, search_files Google Workspace tools (11 tools, auto-registered)
Zoom zoom_create_meeting, zoom_update_meeting, zoom_get_transcript Zoom meeting tools (3 tools, auto-registered)
Telegram telegram_send_message, telegram_send_photo, telegram_get_updates, telegram_set_webhook, telegram_get_me, telegram_get_chat Telegram Bot API tools (6 tools, auto-registered)
Twilio send_sms, send_whatsapp, list_messages, get_message SMS and WhatsApp tools (4 tools, auto-registered)
Multimedia generate_image, generate_video, video_status, text_to_speech, speech_to_text Vendor-dependent subset auto-registered for OpenAI, Google, and xAI connectors
Self-learning memory Six read + six write memory_* tools Graph, vector, document, profile, and controlled mutation tools supplied by memory plugins

Memory, In-Context Memory, and Persistent Instructions tools are documented in their respective sections above. Multimedia tools are documented in the Audio, Image, and Video sections. Desktop tools are documented in the Desktop Automation Tools section below. Connector-generated names are always prefixed with the actual connector name.

Store Tools (Memory, Context, Instructions, User Info, Workspace)

All plugin stores are accessed through the 5 unified store_* tools. Each store becomes available when its corresponding feature flag is enabled:

typescript
const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  context: {
    model: 'gpt-4.1',
    features: {
      workingMemory: true,       // enables store="notes"
      inContextMemory: true,     // enables store="whiteboard"
      persistentInstructions: true, // enables store="instructions"
      userInfo: true,            // enables store="user_info"
      sharedWorkspace: true,     // enables store="workspace"
    },
  },
});

// The LLM uses these 5 tools to interact with all stores:
// - store_get(store, key?)       — get one entry or all
// - store_set(store, key, data)  — create/update entry
// - store_delete(store, key)     — remove entry
// - store_list(store, filter?)   — list entries
// - store_action(store, action, params?) — store-specific operations (query, clear, etc.)

// Examples:
// store_set("notes", "user.profile", { description: "User profile", value: { name: "Alice" }, priority: "high" })
// store_get("notes", "user.profile")
// store_set("whiteboard", "state", { description: "Current state", value: { step: 1 } })
// store_set("instructions", "tone", { content: "Be concise" })
// store_action("notes", "query", { tier: "findings" })

// Programmatic access (unchanged):
const memory = agent.context.memory;
await memory.store('user.profile', 'User profile', { name: 'Alice' }, 'high');
const profile = await memory.retrieve('user.profile');

Context Budget

Access context budget information via prepare():

typescript
const { input, budget } = await agent.context.prepare();

console.log(budget);
// {
//   maxTokens: 128000,
//   totalUsed: 45000,
//   available: 63800,
//   utilizationPercent: 35.2,
//   breakdown: {
//     systemPrompt: 500,
//     pluginInstructions: 800,
//     conversation: 38000,
//     currentInput: 200,
//   }
// }

Code Execution Tool

typescript
import { createExecuteJavaScriptTool } from 'oneringai';

const jsTool = createExecuteJavaScriptTool();

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: [jsTool],
});

const response = await agent.run('Calculate the sum of numbers from 1 to 100');

// Agent will:
// 1. Generate JavaScript code
// 2. Execute: execute_javascript({ code: 'Array(100).fill(0).map((_, i) => i+1).reduce((a,b) => a+b)' })
// 3. Return result: 5050

Custom Tool Generation

A complete meta-tool system that enables agents to create, test, iterate, and persist their own reusable tools at runtime. Instead of pre-defining every tool, agents can generate new tools on-the-fly based on user requests, test them in a sandboxed VM, save them to disk, and load them later — across sessions and agents.

Overview

Meta-Tool Purpose Safe by Default
custom_tool_draft Validate name, schema, code syntax Yes
custom_tool_test Execute code in VM sandbox with test input No
custom_tool_save Persist to ~/.oneringai/users/<userId>/custom-tools/ No
custom_tool_list Search saved tools (name, description, tags, category) Yes
custom_tool_load Retrieve full definition including code Yes
custom_tool_delete Remove from storage No

Additionally:

  • hydrateCustomTool() — Convert a saved definition into a live ToolFunction
  • createCustomToolMetaTools() — Bundle factory that creates all 6 tools with shared storage

Quick Start

typescript
import { Agent, Connector, Vendor, createCustomToolMetaTools } from 'oneringai';

Connector.create({
  name: 'openai',
  vendor: Vendor.OpenAI,
  auth: { type: 'api_key', apiKey: process.env.OPENAI_API_KEY! },
});

// Give the agent the ability to create tools
const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: [...createCustomToolMetaTools()],
});

// Ask the agent to create a tool
const response = await agent.run(
  'Create a tool called "celsius_to_fahrenheit" that converts temperature from Celsius to Fahrenheit'
);

// The agent will autonomously:
// 1. Call custom_tool_draft to validate the definition
// 2. Call custom_tool_test with sample inputs to verify
// 3. Call custom_tool_save to persist it

The Agent Workflow: Draft → Test → Save

When an agent has the custom tool meta-tools registered, it follows this natural workflow:

Step 1: Draft — The agent generates the tool definition and validates it:

text
Agent calls: custom_tool_draft({
  name: "celsius_to_fahrenheit",
  description: "Converts temperature from Celsius to Fahrenheit",
  inputSchema: {
    type: "object",
    properties: { celsius: { type: "number" } },
    required: ["celsius"]
  },
  code: "output = { fahrenheit: input.celsius * 9/5 + 32 };"
})
→ Returns: { success: true, validated: { ... } }

Step 2: Test — The agent tests the code with sample inputs:

text
Agent calls: custom_tool_test({
  code: "output = { fahrenheit: input.celsius * 9/5 + 32 };",
  inputSchema: { type: "object", properties: { celsius: { type: "number" } } },
  testInput: { celsius: 100 }
})
→ Returns: { success: true, result: { fahrenheit: 212 }, logs: [], executionTime: 3 }

If the test fails, the agent fixes the code and tests again — iterating until it works.

Step 3: Save — The agent persists the validated, tested tool:

text
Agent calls: custom_tool_save({
  name: "celsius_to_fahrenheit",
  description: "Converts temperature from Celsius to Fahrenheit",
  inputSchema: { ... },
  code: "output = { fahrenheit: input.celsius * 9/5 + 32 };",
  tags: ["conversion", "temperature"],
  category: "math"
})
→ Returns: { success: true, name: "celsius_to_fahrenheit", storagePath: "~/.oneringai/users/default/custom-tools/" }

Dynamic Descriptions & Connector Awareness

The custom_tool_draft and custom_tool_test tools use descriptionFactory to generate dynamic descriptions that include:

  1. The full VM sandbox API — authenticatedFetch, fetch, connectors.list(), globals, etc.
  2. All currently registered connectors — names, service types, base URLs, descriptions

This means the agent always knows what APIs are available when writing custom tool code. The description is regenerated every time tool definitions are sent to the LLM, so it stays current when connectors are added or removed.

Example of what the agent sees in the tool description:

text
SANDBOX API (available inside custom tool code):

1. authenticatedFetch(url, options, connectorName)
   Makes authenticated HTTP requests using the connector's credentials.
   Auth headers are added automatically — DO NOT set Authorization header manually.
   ...

REGISTERED CONNECTORS:
   • "github" (GitHub)
     Service: github
     URL: https://api.github.com

   • "slack" (Slack Workspace)
     Service: slack
     URL: https://slack.com/api

Creating API-Connected Tools

Because the sandbox provides authenticatedFetch, agents can create tools that call external APIs through registered connectors:

typescript
// Agent creates a tool that fetches GitHub repos
// (assuming a 'github' connector is registered)
await agent.run(
  'Create a tool called "list_repos" that lists all repositories for a given GitHub user. ' +
  'Use the github connector.'
);

// The agent will write code like:
// const resp = await authenticatedFetch(`/users/${input.username}/repos`, { method: 'GET' }, 'github');
// const repos = await resp.json();
// output = repos.map(r => ({ name: r.full_name, stars: r.stargazers_count }));

Loading and Using Saved Tools

Saved tools can be loaded programmatically and used by any agent:

typescript
import {
  createFileCustomToolStorage,
  hydrateCustomTool,
  Agent,
} from 'oneringai';

// Load a saved tool (undefined = default user)
const storage = createFileCustomToolStorage();
const definition = await storage.load(undefined, 'celsius_to_fahrenheit');

if (definition) {
  // Hydrate into a live ToolFunction
  const tool = hydrateCustomTool(definition);

  // Register on any agent
  const agent = Agent.create({ connector: 'openai', model: 'gpt-4.1', tools: [tool] });

  // The agent can now use celsius_to_fahrenheit like any built-in tool
  const response = await agent.run('Convert 37°C to Fahrenheit');
}

Searching Saved Tools

The storage layer supports filtering and search:

typescript
const storage = createFileCustomToolStorage();
const userId = undefined; // or a specific userId for multi-tenant apps

// List all saved tools
const all = await storage.list(userId);

// Search by name or description
const mathTools = await storage.list(userId, { search: 'convert' });

// Filter by tags
const apiTools = await storage.list(userId, { tags: ['api'] });

// Filter by category
const networkTools = await storage.list(userId, { category: 'network' });

// Pagination
const page2 = await storage.list(userId, { limit: 10, offset: 10 });

Custom Storage Backends

The default FileCustomToolStorage persists tools to ~/.oneringai/users/<userId>/custom-tools/ (defaults to ~/.oneringai/users/default/custom-tools/ when no userId). For production systems, implement ICustomToolStorage with any backend:

typescript
import type { ICustomToolStorage, CustomToolListOptions } from 'oneringai';
import type { CustomToolDefinition, CustomToolSummary } from 'oneringai';

class MongoCustomToolStorage implements ICustomToolStorage {
  constructor(private db: Db) {}

  async save(userId: string | undefined, definition: CustomToolDefinition): Promise<void> {
    const user = userId || 'default';
    await this.db.collection('custom_tools').replaceOne(
      { userId: user, name: definition.name },
      { userId: user, ...definition },
      { upsert: true }
    );
  }

  async load(userId: string | undefined, name: string): Promise<CustomToolDefinition | null> {
    const user = userId || 'default';
    return this.db.collection('custom_tools').findOne({ userId: user, name });
  }

  async delete(userId: string | undefined, name: string): Promise<void> {
    const user = userId || 'default';
    await this.db.collection('custom_tools').deleteOne({ userId: user, name });
  }

  async exists(userId: string | undefined, name: string): Promise<boolean> {
    const user = userId || 'default';
    return (await this.db.collection('custom_tools').countDocuments({ userId: user, name })) > 0;
  }

  async list(userId: string | undefined, options?: CustomToolListOptions): Promise<CustomToolSummary[]> {
    const user = userId || 'default';
    const filter: any = { userId: user };
    if (options?.tags?.length) filter['metadata.tags'] = { $in: options.tags };
    if (options?.category) filter['metadata.category'] = options.category;
    if (options?.search) {
      filter.$or = [
        { name: { $regex: options.search, $options: 'i' } },
        { description: { $regex: options.search, $options: 'i' } },
      ];
    }

    return this.db.collection('custom_tools')
      .find(filter, { projection: { code: 0 } })  // Exclude code for summaries
      .sort({ updatedAt: -1 })
      .skip(options?.offset ?? 0)
      .limit(options?.limit ?? 100)
      .toArray();
  }

  getPath(userId: string | undefined): string { return `mongodb://custom_tools/${userId || 'default'}`; }
}

// Use with meta-tools
import { createCustomToolMetaTools } from 'oneringai';
const storage = new MongoCustomToolStorage(db);
const tools = createCustomToolMetaTools({ storage });

Sandbox API Reference

Custom tool code runs in the same VM sandbox as execute_javascript. Available APIs:

API Description
input The tool's input arguments (matches inputSchema)
output Set this to return the tool's result
authenticatedFetch(url, options, connectorName) Authenticated HTTP request via a registered connector
fetch(url, options) Standard fetch (no authentication)
connectors.list() Array of available connector names
connectors.get(name) Connector info: { displayName, description, baseURL, serviceType }
console.log/error/warn Captured in logs (returned in test results)
JSON, Math, Date, Buffer, Promise Standard globals
Array, Object, String, Number, Boolean Built-in types
RegExp, Map, Set, Error, URL, URLSearchParams Utility types
TextEncoder, TextDecoder Text encoding
setTimeout, setInterval Timers

Limitations: No file system access, no require/import, code runs in async context (top-level await is available).

Tool Definition Schema

typescript
interface CustomToolDefinition {
  version: number;                         // Format version (currently 1)
  name: string;                            // Unique name (/^[a-z][a-z0-9_]*$/)
  displayName?: string;                    // Human-readable name
  description: string;                     // What the tool does
  inputSchema: Record<string, unknown>;    // JSON Schema (must have type: 'object')
  outputSchema?: Record<string, unknown>;  // Optional output schema (documentation only)
  code: string;                            // JavaScript code for the VM sandbox
  createdAt: string;                       // ISO timestamp
  updatedAt: string;                       // ISO timestamp
  metadata?: {
    tags?: string[];                       // Categorization tags
    category?: string;                     // Category grouping
    author?: string;                       // Creator identifier
    generationPrompt?: string;             // The prompt used to create this tool
    testCases?: CustomToolTestCase[];      // Saved test cases
    requiresConnector?: boolean;           // Whether connectors are needed
    connectorNames?: string[];             // Which connectors the tool uses
  };
}

ToolManager Metadata

When registering hydrated custom tools, you can set provenance metadata:

typescript
const tool = hydrateCustomTool(definition);

agent.tools.register(tool, {
  source: 'custom',            // Track origin (built-in, connector, custom, mcp)
  tags: ['api', 'weather'],    // Categorization
  category: 'external-apis',   // Grouping
});

These fields are preserved through getState()/loadState() for session persistence.

Storage Path

text
~/.oneringai/users/<userId>/custom-tools/
├── _index.json              # Index for fast listing and search
├── celsius_to_fahrenheit.json
├── fetch_weather.json
└── list_repos.json

Developer Tools (Filesystem & Shell)

A comprehensive set of tools for file system operations and shell command execution, inspired by Claude Code. Perfect for building coding assistants, DevOps agents, or any agent that needs to interact with the local filesystem.

Quick Start

typescript
import { developerTools } from 'oneringai';

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: developerTools, // All 11 tools included (filesystem, bash, dev server, background process management)
});

// Agent can now read, write, edit files, search, and run commands
await agent.run('Read the package.json and tell me the version');

Individual Tools

You can also import and configure tools individually:

typescript
import {
  createReadFileTool,
  createWriteFileTool,
  createEditFileTool,
  createGlobTool,
  createGrepTool,
  createListDirectoryTool,
  createBashTool,
} from 'oneringai';

// Create tools with custom configuration
const readFile = createReadFileTool({
  workingDirectory: '/path/to/project',
  maxFileSize: 5 * 1024 * 1024, // 5MB
});

const bash = createBashTool({
  workingDirectory: '/path/to/project',
  defaultTimeout: 60000, // 1 minute
  allowBackground: true,
});

Filesystem Tools

read_file

Read file contents with line numbers. Automatically converts binary document formats (PDF, DOCX, XLSX, PPTX, ODT, ODP, ODS, RTF, PNG, JPG, GIF, WEBP) to markdown text via Document Reader.

typescript
// Text files — returns content with line numbers
read_file({
  file_path: '/path/to/file.ts',
  offset: 50,    // Start at line 50 (optional)
  limit: 100,    // Read 100 lines (optional)
});
// Returns: { success: true, content: "1\tconst x = 1;...", lines: 100 }

// Binary documents — auto-converted to markdown
read_file({ file_path: '/path/to/report.pdf' });
// Returns: { success: true, content: "# Document: report.pdf\n...", encoding: 'document' }
write_file

Create or overwrite files. Automatically creates parent directories.

typescript
write_file({
  file_path: '/path/to/new/file.ts',
  content: 'export const hello = "world";',
});
// Returns: { success: true, created: true, bytesWritten: 29 }
edit_file

Surgical find-and-replace edits. Ensures uniqueness to prevent unintended changes.

typescript
edit_file({
  file_path: '/path/to/file.ts',
  old_string: 'const x = 1;',
  new_string: 'const x = 42;',
  replace_all: false, // Fails if old_string is not unique (default)
});
// Returns: { success: true, replacements: 1 }
glob

Find files by pattern.

typescript
glob({
  pattern: '**/*.ts',
  path: '/path/to/project', // Optional, defaults to cwd
});
// Returns: { success: true, files: ['src/index.ts', 'src/utils.ts', ...], count: 15 }
grep

Search file contents with regex.

typescript
grep({
  pattern: 'function\\s+\\w+',
  path: '/path/to/project',
  type: 'ts',                      // Filter by file type
  output_mode: 'content',          // 'content', 'files_with_matches', 'count'
  case_insensitive: true,
  context_before: 2,               // Lines before match
  context_after: 2,                // Lines after match
});
// Returns: { success: true, matches: [...], filesMatched: 5, totalMatches: 23 }
list_directory

List directory contents with metadata.

typescript
list_directory({
  path: '/path/to/project',
  recursive: true,
  filter: 'files',     // 'files' or 'directories'
  max_depth: 3,
});
// Returns: { success: true, entries: [...], count: 42 }

Shell Tool

bash

Execute shell commands with timeout and safety features.

typescript
bash({
  command: 'npm install',
  timeout: 300000,        // 5 minutes
  description: 'Install dependencies',
  run_in_background: false,
});
// Returns: { success: true, stdout: '...', exitCode: 0, duration: 5234 }

Safety Features:

  • Blocks dangerous commands (rm -rf /, fork bombs, etc.)
  • Configurable timeout (default 2 min, max 10 min)
  • Output truncation for large outputs
  • Background execution support

Blocked Commands:

  • rm -rf / and rm -rf /*
  • Fork bombs (:(){:|:&};:)
  • /dev/sda writes
  • (git operations are not runtime-blocked — the tool description only instructs the agent to avoid risky git operations like force-push/reset --hard without permission)

Configuration Options

All filesystem tools share common configuration:

typescript
interface FilesystemToolConfig {
  workingDirectory?: string;       // Base directory (default: cwd)
  allowedDirectories?: string[];   // Restrict to these directories
  blockedDirectories?: string[];   // Block access (default: node_modules, .git)
  maxFileSize?: number;            // Max read size (default: 10MB)
  maxResults?: number;             // Max results for glob/grep (default: 1000)
  followSymlinks?: boolean;        // Follow symlinks (default: false)
  excludeExtensions?: string[];    // Skip binary files
}

Shell tool configuration:

typescript
interface ShellToolConfig {
  workingDirectory?: string;       // Working directory
  defaultTimeout?: number;         // Default timeout (default: 120000ms)
  maxTimeout?: number;             // Max timeout (default: 600000ms)
  maxOutputSize?: number;          // Max output size (default: 10MB)
  allowBackground?: boolean;       // Allow background execution (default: true)
  shell?: string;                  // Shell to use (default: /bin/bash)
  env?: Record<string, string>;    // Environment variables
}

Best Practices

  1. Use edit_file for code changes - Never rewrite entire files; use surgical edits
  2. Prefer glob over bash find - More efficient and safer
  3. Prefer grep over bash grep - Better output formatting and safety
  4. Set working directory - Restrict operations to project directory
  5. Configure blockedDirectories - Prevent accidental access to sensitive directories

Desktop Automation Tools

OS-level desktop automation for "computer use" agent loops. Enables agents to see and interact with the desktop: take screenshots, move the mouse, click, type, press keyboard shortcuts, and manage windows.

Quick Start

typescript
import { desktopTools, Agent, Connector, Vendor } from 'oneringai';

// Setup
Connector.create({
  name: 'openai',
  vendor: Vendor.OpenAI,
  auth: { type: 'api_key', apiKey: process.env.OPENAI_API_KEY! },
});

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: desktopTools,
});

// The agent loop: screenshot → vision model → tool calls → repeat
await agent.run('Take a screenshot, then open the Calculator app and compute 42 * 17');

Prerequisites

Desktop tools require @nut-tree-fork/nut-js as an optional peer dependency:

bash
npm install @nut-tree-fork/nut-js

On macOS, you must grant accessibility permissions to your terminal app: System Settings → Privacy & Security → Accessibility → Enable your terminal/IDE.

Available Tools

Tool Args Description
desktop_screenshot region? Capture full screen or a specific region. Returns image to vision model via __images convention.
desktop_mouse_move x, y Move the cursor to a position (in screenshot pixel coords).
desktop_mouse_click x?, y?, button?, clickCount? Click at position or current location. Supports left/right/middle, single/double/triple.
desktop_mouse_drag startX, startY, endX, endY, button? Drag from start to end position.
desktop_mouse_scroll deltaX?, deltaY?, x?, y? Scroll wheel. Positive deltaY = down, negative = up.
desktop_get_cursor (none) Get current cursor position in screenshot coords.
desktop_keyboard_type text, delay? Type text as keyboard input.
desktop_keyboard_key keys Press key combo (e.g., "ctrl+c", "cmd+shift+s", "enter").
desktop_get_screen_size (none) Get physical/logical dimensions and scale factor.
desktop_window_list (none) List visible windows with IDs, titles, and bounds.
desktop_window_focus windowId Bring a window to the foreground.

Coordinate System

All coordinates use physical pixel space (screenshot space). This means the coordinates you see in a screenshot image are the exact coordinates you pass to mouse tools. The driver handles Retina/HiDPI scaling internally.

text
Screenshot pixels (2560×1600 on Retina) ← Tools accept these coords
         ↕ driver divides by scaleFactor
OS logical coords (1280×800)             ← nut-tree operates here

Configuration

typescript
import { createDesktopScreenshotTool, createDesktopMouseClickTool } from 'oneringai';

// Custom config for individual tools
const screenshot = createDesktopScreenshotTool({
  humanDelay: [0, 0],        // No delay (instant actions)
  humanizeMovement: false,    // Straight-line mouse movement
});

// Or provide a custom driver implementation
import type { IDesktopDriver } from 'oneringai';

declare const myDriver: IDesktopDriver; // Supply a complete host implementation of the interface.
const click = createDesktopMouseClickTool({ driver: myDriver });

The __images Convention

The desktop_screenshot tool returns an __images array in its result. This is automatically handled by all provider converters:

  • Anthropic: Image blocks inside tool_result content
  • OpenAI: Follow-up user message with input_image
  • Google: inlineData parts alongside functionResponse

The base64 image data is stripped from the text content to save tokens — only the image blocks are sent to the vision model.

Key Combo Syntax

The desktop_keyboard_key tool accepts key combo strings with + as separator:

  • Modifiers: ctrl, cmd/command/meta, alt/option, shift
  • Special keys: enter, tab, escape, backspace, delete, space
  • Arrow keys: up, down, left, right
  • Function keys: f1 through f12
  • Navigation: home, end, pageup, pagedown
  • Letters/digits: a-z, 0-9

Examples: "ctrl+c", "cmd+shift+s", "alt+tab", "enter", "f5"

Document Reader

Universal file-to-LLM-content converter. Reads arbitrary document formats (Office, PDF, spreadsheets, HTML, text, images) from any source and produces clean markdown text with optional image extraction.

How It Works

The Document Reader is integrated at three levels:

  1. read_file tool — Agents calling read_file on binary documents (PDF, DOCX, XLSX, PPTX, etc.) automatically get markdown text. No code changes needed.
  2. web_fetch tool — Documents downloaded from URLs (detected via Content-Type or extension) are auto-converted to markdown.
  3. Programmatic API — DocumentReader.read() and readDocumentAsContent() for direct use in application code.

Supported Formats

Format Extensions Handler Library
Office .docx, .pptx, .odt, .odp, .ods, .rtf OfficeHandler officeparser (lazy-loaded)
Spreadsheet .xlsx, .csv ExcelHandler exceljs (lazy-loaded)
PDF .pdf PDFHandler unpdf (lazy-loaded)
HTML .html, .htm HTMLHandler Readability + Turndown (built-in)
Text .txt, .md, .json, .xml, .yaml, .yml TextHandler none
Image .png, .jpg, .jpeg, .gif, .webp, .svg ImageHandler none

All heavy dependencies are lazy-loaded via dynamic import() — they are only loaded when a document of that type is first read, keeping startup fast.

Quick Start — Agent with read_file

The simplest way to use Document Reader is through agents with developer tools:

typescript
import { Agent, developerTools } from 'oneringai';

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: developerTools,
});

// read_file auto-detects binary formats and converts to markdown
await agent.run('Read /path/to/quarterly-report.pdf and summarize it');
await agent.run('Read /path/to/sales-data.xlsx and identify the top performers');
await agent.run('Read /path/to/onboarding.pptx and list the key steps');
await agent.run('Read /path/to/contract.docx and highlight important clauses');

The read_file tool automatically detects binary document formats (PDF, DOCX, XLSX, PPTX, ODT, ODP, ODS, RTF, PNG, JPG, GIF, WEBP) and converts them to markdown. Text files (.txt, .md, .json, .xml, .yaml, .csv, .html) continue to be read as UTF-8 text as before.

Quick Start — web_fetch with Documents

The web_fetch tool auto-detects document Content-Types and URL extensions:

typescript
import { Agent, tools } from 'oneringai';

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: [tools.webFetch],
});

// web_fetch detects PDF content-type and converts to markdown
await agent.run('Fetch https://example.com/annual-report.pdf and summarize it');

When web_fetch detects a document MIME type (application/pdf, Office MIME types, etc.) or a document URL extension (.pdf, .docx, .xlsx), it downloads the file and converts it to markdown using DocumentReader. The result has contentType: 'document' in the response.

Programmatic Usage — DocumentReader

For application code (outside of tool context):

typescript
import { DocumentReader, mergeTextPieces } from 'oneringai';

// Create reader with defaults
const reader = DocumentReader.create({
  defaults: {
    maxTokens: 50_000,
    extractImages: true,
    imageFilter: { minWidth: 100, minHeight: 100 },
  },
});

// Read from different sources
const result = await reader.read('/path/to/report.pdf');
const resultVariant2 = await reader.read('https://example.com/doc.xlsx');
const resultVariant3 = await reader.read({ type: 'buffer', buffer: uploadedBuffer, filename: 'doc.docx' });
const resultVariant4 = await reader.read({ type: 'blob', blob: fileBlob, filename: 'slides.pptx' });

// Get merged text
const markdown = mergeTextPieces(resultVariant4.pieces);
console.log(markdown);

// Access individual pieces
for (const piece of resultVariant4.pieces) {
  if (piece.type === 'text') {
    console.log(`[${piece.metadata.section}] ${piece.content.substring(0, 100)}...`);
  } else {
    console.log(`[Image] ${piece.mimeType} (${piece.metadata.sizeBytes} bytes)`);
  }
}

// Metadata
console.log(resultVariant4.metadata.format);           // 'pdf'
console.log(resultVariant4.metadata.family);           // 'pdf'
console.log(resultVariant4.metadata.estimatedTokens);  // 12500
console.log(resultVariant4.metadata.totalPieces);      // 15
console.log(resultVariant4.metadata.totalImagePieces); // 3
console.log(resultVariant4.metadata.processingTimeMs); // 234

Content Bridge — readDocumentAsContent()

For multimodal LLM input (text + images), use the content bridge:

typescript
import { readDocumentAsContent, documentToContent } from 'oneringai';

// One-call convenience: read + filter + convert to Content[]
const content = await readDocumentAsContent('/path/to/slides.pptx', {
  extractImages: true,
  imageDetail: 'auto',
  imageFilter: { minWidth: 100, minHeight: 100 },
  maxImages: 20,
  mergeAdjacentText: true,
});

// Use with agent
const response = await agent.run([
  { type: 'input_text', text: 'Analyze this presentation:' },
  ...content,
]);

// Or use documentToContent() for two-step conversion
const reader = DocumentReader.create();
const result = await reader.read('/path/to/doc.pdf');
const contentVariant2 = documentToContent(result, {
  imageDetail: 'low',
  maxImages: 10,
});

Output Content types:

  • DocumentTextPiece → InputTextContent { type: 'input_text', text }
  • DocumentImagePiece → InputImageContent { type: 'input_image_url', image_url: { url: 'data:...' } }

Read Options

All options are configurable at two levels — reader creation time (defaults) and per-call:

typescript
const reader = DocumentReader.create({
  defaults: {
    maxTokens: 100_000,         // Max estimated tokens in output
    maxOutputBytes: 5_242_880,  // Max output size (5MB)
    extractImages: true,        // Extract images from documents
    imageDetail: 'auto',        // Image detail for LLM ('auto', 'low', 'high')
    imageFilter: {
      minWidth: 50,             // Skip images narrower than this
      minHeight: 50,            // Skip images shorter than this
      minSizeBytes: 1024,       // Skip images smaller than this
      maxImages: 50,            // Max images to keep
      excludePatterns: [/logo/i, /icon/i],  // Exclude by label pattern
    },
    formatOptions: {
      excel: {
        maxRows: 1000,          // Max rows per sheet
        maxColumns: 50,         // Max columns per sheet
        tableFormat: 'markdown', // 'markdown', 'csv', or 'json'
        includeFormulas: false,
      },
      pdf: {
        includeMetadata: true,  // Include title, author, page count
      },
      html: {
        maxLength: 50_000,      // Max HTML length to process
      },
      office: {
        includeSpeakerNotes: true, // Include PPTX speaker notes
      },
    },
  },
  maxDownloadSizeBytes: 50_000_000, // Max file size for URL sources (50MB)
  downloadTimeoutMs: 60_000,        // Download timeout for URL sources
});

// Per-call override
const result = await reader.read('/path/to/large.pdf', {
  maxTokens: 200_000,       // Override just for this call
  extractImages: false,      // Text-only for this call
  pages: [1, 2, 3],         // Only read specific pages
});

Format-Specific Behavior

PPTX/ODP (Presentations):

  • Split into per-slide pieces with ### Slide N headers
  • Speaker notes included by default (configurable)
  • Images extracted from slides as separate pieces

XLSX (Spreadsheets):

  • Each sheet becomes a separate piece with ## Sheet: Name header
  • Four table output formats: markdown (default), markdown-kv, csv, json
  • Typed cell values (dates, numbers, formulas)
  • Row/column limits configurable

PDF:

  • Per-page text pieces with Page N sections
  • Document metadata (title, author, pages) included as first piece
  • Embedded XObject image pixels are encoded as real PNG (RGB, grayscale and alpha)
  • Page selection applies to text and images; extractImages: false skips image work
  • One private input copy/proxy is shared for extraction and destroyed on exit; caller buffers remain reusable
  • Metadata or individual image-page failures log warnings while preserving available text and other pages; inline image/operator coverage follows unpdf

CSV:

  • Auto-detected as spreadsheet family
  • Converted to markdown table by default

Images:

  • Pass-through as base64 image pieces
  • SVG files produce both an image piece and a text piece (SVG source)

JSON/XML/YAML:

  • Wrapped in fenced code blocks with language tags

Transformer Pipeline

Documents pass through a configurable transformer pipeline after extraction:

Transformer Priority Applies To Description
documentHeaderTransformer 10 All Prepends # Document: filename with format and size
tableFormattingTransformer 50 xlsx, csv Normalizes markdown table column alignment
truncationTransformer 1000 All Enforces maxTokens limit, truncates at paragraph boundaries

Custom transformers:

typescript
import type { IDocumentTransformer } from 'oneringai';

const myTransformer: IDocumentTransformer = {
  name: 'addWatermark',
  appliesTo: [],  // empty = all formats
  priority: 20,
  async transform(pieces, context) {
    // Add a watermark to each text piece
    return pieces.map(p => p.type === 'text'
      ? { ...p, content: p.content + '\n\n_Processed by MyApp_' }
      : p
    );
  },
};

const result = await reader.read('/path/to/doc.pdf', {
  transformers: [myTransformer],
});

Custom Format Handlers

Replace or extend built-in handlers:

typescript
import type { IFormatHandler } from 'oneringai';

const customPDFHandler: IFormatHandler = {
  name: 'MyPDFHandler',
  supportedFormats: ['pdf'],
  async handle(buffer, filename, format, options) {
    // Custom PDF parsing logic
    const text = await parsePdfText(buffer, options); // Your parser implementation.
    return [{ type: 'text', content: text, metadata: {
      sourceFilename: filename, format, index: 0, sizeBytes: buffer.length,
      estimatedTokens: Math.ceil(text.length / 4),
    } }];
  },
};

const reader = DocumentReader.create();
reader.registerHandler('pdf', customPDFHandler);

Image Filtering

Image filtering removes small/junk images (logos, icons, backgrounds) at two stages:

  1. Extraction time — Applied in DocumentReader.read() after the format handler runs
  2. Content conversion time — Applied in documentToContent() / readDocumentAsContent()
typescript
// Extraction-time filtering (in DocumentReader)
const result = await reader.read('/path/to/slides.pptx', {
  imageFilter: {
    minWidth: 100,       // Skip images narrower than 100px
    minHeight: 100,      // Skip images shorter than 100px
    minSizeBytes: 2048,  // Skip images smaller than 2KB
    maxImages: 30,       // Keep at most 30 images
    excludePatterns: [/logo/i, /background/i],
  },
});

// Content-conversion-time filtering (additional pass)
const content = documentToContent(result, {
  imageFilter: { minWidth: 200 },  // Stricter for LLM input
  maxImages: 10,                    // Fewer images for LLM
  imageDetail: 'low',               // Low detail saves tokens
});

Error Handling

typescript
import { AIError } from 'oneringai';

try {
  const result = await reader.read('/path/to/file.xyz');
} catch (error) {
  if (error instanceof AIError && error.code === 'UNSUPPORTED_FORMAT') {
    console.log(`Format not supported: ${error.message}`);
  } else if (error instanceof AIError && error.code === 'DOCUMENT_READ_ERROR') {
    console.log(`Read failed: ${error.message}`);
  }
}

Constants Reference

All defaults are defined in DOCUMENT_DEFAULTS and can be overridden:

Constant Default Description
MAX_OUTPUT_TOKENS 100,000 Max estimated tokens in output
MAX_OUTPUT_BYTES 5MB Max output size in bytes
MAX_DOWNLOAD_SIZE_BYTES 50MB Max download size for URL sources
DOWNLOAD_TIMEOUT_MS 60,000 Download timeout for URL sources
MAX_EXTRACTED_IMAGES 50 Max images extracted from a single document
MAX_EXCEL_ROWS 1,000 Max rows per Excel sheet
MAX_EXCEL_COLUMNS 50 Max columns per Excel sheet
MAX_HTML_LENGTH 10,000,000 Max HTML content length
CHARS_PER_TOKEN 4 Chars per token estimate
IMAGE_FILTER.MIN_WIDTH 50 Default minimum image width
IMAGE_FILTER.MIN_HEIGHT 50 Default minimum image height
IMAGE_FILTER.MIN_SIZE_BYTES 1,024 Default minimum image size

Web Tools

Tools for fetching web content, searching the web, and scraping pages. tools.webFetch is a connector-free tool; web_search and web_scrape are connector-dependent (via the ConnectorTools pattern).

tools.webFetch

Fetch and process web content. Converts HTML to markdown for easy consumption by LLMs. Also auto-detects document formats (PDF, DOCX, XLSX, etc.) by Content-Type or URL extension and converts them to markdown via Document Reader.

typescript
import { Agent, tools } from 'oneringai';

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: [tools.webFetch],
});

await agent.run('Fetch https://example.com and summarize it');

Parameters:

  • url (required) — URL to fetch
  • userAgent — Custom user agent string (default: a generic bot user agent)
  • timeout — Timeout in milliseconds (default: 10000)

web_search (ConnectorTools)

Web search via ConnectorTools pattern. Create a connector with a search service type, then use ConnectorTools.for() to get the tools.

Supported service types: serper, brave-search, tavily, rapidapi-search

typescript
import { Connector, ConnectorTools, Agent, tools } from 'oneringai';

// Create a search connector
Connector.create({
  name: 'serper',
  serviceType: 'serper',
  auth: { type: 'api_key', apiKey: process.env.SERPER_API_KEY! },
  baseURL: 'https://google.serper.dev',
});

// Get search tools from the connector
const searchTools = ConnectorTools.for('serper');

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: [tools.webFetch, ...searchTools],
});

await agent.run('Search for the latest Node.js release');

Parameters:

  • query (required) — Search query
  • numResults — Number of results (default: 10)
  • country — Country/region code (e.g., "us", "gb")
  • language — Language code (e.g., "en", "fr")

web_scrape (ConnectorTools)

Web scraping via ConnectorTools pattern. Tries native fetch first, falls back to the bound scrape provider.

Supported service types: zenrows, jina-reader, firecrawl, scrapingbee

typescript
import { Connector, ConnectorTools } from 'oneringai';

Connector.create({
  name: 'zenrows',
  serviceType: 'zenrows',
  auth: { type: 'api_key', apiKey: process.env.ZENROWS_API_KEY! },
  baseURL: 'https://api.zenrows.com/v1',
});

const scrapeTools = ConnectorTools.for('zenrows');

Parameters:

  • url (required) — URL to scrape
  • includeMarkdown — Convert to markdown
  • includeLinks — Extract links
  • includeHtml — Include raw HTML
  • waitForSelector — CSS selector to wait for
  • timeout — Timeout in milliseconds

JSON Tool

tools.jsonManipulator

Manipulate JSON objects — add, delete, or replace fields.

typescript
import { Agent, tools } from 'oneringai';

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: [tools.jsonManipulator],
});

await agent.run('Add a "version" field set to "2.0" to this JSON: {"name": "app"}');

Parameters:

  • object (required) — JSON object to manipulate (not a string)
  • operation (required) — "add", "delete", or "replace"
  • path (required) — JSON path (dot notation, e.g., "config.debug")
  • value — Value for add/replace operations

GitHub Connector Tools

When a GitHub connector is configured, ConnectorTools.for('github') automatically includes 8 dedicated tools alongside the generic API tool (including list_branches, not otherwise documented here). These mirror the local filesystem tools for remote GitHub repositories.

Quick Start

typescript
import { Connector, ConnectorTools, Services, Agent } from 'oneringai';

// Create a GitHub connector
Connector.create({
  name: 'github',
  serviceType: Services.Github,
  auth: { type: 'api_key', apiKey: process.env.GITHUB_TOKEN! },
  baseURL: 'https://api.github.com',
  options: {
    defaultRepository: 'myorg/myrepo', // Optional: default repo for all tools
  },
});

// Get all GitHub tools (generic API + 8 dedicated tools)
const tools = ConnectorTools.for('github');

// Use with an agent
const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: tools,
});

// Agent can now search files, read code, analyze PRs, and create PRs
await agent.run('Find all TypeScript files in src/ and show me the main entry point');
await agent.run('Show me PR #42 and summarize the changes');

Repository Resolution

All GitHub tools accept an optional repository parameter. Resolution order:

  1. Explicit parameter: { "repository": "owner/repo" } or { "repository": "https://github.com/owner/repo" }
  2. Connector default: connector.options.defaultRepository
  3. Error: If neither is available

This means you can configure a default repo once on the connector and all tools use it automatically.

search_files

Search for files by glob pattern in a repository. Mirrors the local glob tool.

jsonc
// Find TypeScript files
{ "pattern": "**/*.ts" }

// Search in specific path
{ "pattern": "src/components/**/*.tsx", "repository": "facebook/react" }

// Search specific branch
{ "pattern": "**/*.test.ts", "ref": "develop" }

Parameters:

  • repository — Repository in "owner/repo" format or GitHub URL (optional if connector has default)
  • pattern (required) — Glob pattern (**/*.ts, src/**/*.tsx, etc.)
  • ref — Branch, tag, or commit SHA (defaults to default branch)

Returns: { files: [{ path, size, type }], count, truncated }

search_code

Search code content across a repository. Mirrors the local grep tool.

jsonc
// Find function usage
{ "query": "handleAuth", "language": "typescript" }

// Search in specific path
{ "query": "TODO", "path": "src/utils", "extension": "ts" }

Parameters:

  • repository — Repository (optional)
  • query (required) — Search term
  • language — Filter by language ("typescript", "python", etc.)
  • path — Filter by path prefix ("src/")
  • extension — Filter by extension ("ts", "py")
  • limit — Max results (default: 30, max: 100)

Returns: { matches: [{ file, fragment }], count, truncated }

Note: GitHub's code search API is rate-limited to 30 requests per minute.

read_file (GitHub)

Read file content from a repository with line range support. Mirrors the local read_file tool.

jsonc
// Read entire file
{ "path": "src/index.ts" }

// Read specific lines
{ "path": "src/app.ts", "offset": 100, "limit": 50 }

// Read from specific branch
{ "path": "README.md", "ref": "develop" }

Parameters:

  • repository — Repository (optional)
  • path (required) — File path within the repo
  • ref — Branch, tag, or SHA
  • offset — Start line (1-indexed)
  • limit — Number of lines (default: 2000)

Returns: { content: "1\tline one\n2\tline two...", lines, size, truncated, sha }

Output is formatted with line numbers matching the local read_file format. Files larger than 1MB are automatically fetched via the Git Blob API.

get_pr

Get full details of a pull request.

jsonc
{ "pull_number": 42 }
{ "pull_number": 42, "repository": "owner/repo" }

Parameters:

  • repository — Repository (optional)
  • pull_number (required) — PR number

Returns: { data: { number, title, body, state, draft, author, labels, reviewers, mergeable, head, base, url, created_at, updated_at, additions, deletions, changed_files } }

pr_files

Get files changed in a PR with diffs.

jsonc
{ "pull_number": 42 }

Parameters:

  • repository — Repository (optional)
  • pull_number (required) — PR number

Returns: { files: [{ filename, status, additions, deletions, changes, patch }], count }

The status field is one of: added, modified, removed, renamed. The patch field contains the unified diff.

pr_comments

Get all comments and reviews on a PR, merged from three GitHub API endpoints into a unified format.

jsonc
{ "pull_number": 42 }

Parameters:

  • repository — Repository (optional)
  • pull_number (required) — PR number

Returns: { comments: [{ id, type, author, body, created_at, path?, line?, state? }], count }

Comment types:

  • review_comment — Line-level comments on code (includes path and line)
  • review — Full reviews (approve/request changes/comment, includes state)
  • comment — General PR comments

All entries are sorted by creation date (oldest first).

create_pr

Create a pull request.

jsonc
// Basic PR
{ "title": "Add feature", "head": "feature-branch", "base": "main" }

// Draft PR with description
{
  "title": "WIP: Refactor auth",
  "body": "## Changes\n- Refactored auth flow\n- Added tests",
  "head": "refactor/auth",
  "base": "main",
  "draft": true
}

Parameters:

  • repository — Repository (optional)
  • title (required) — PR title
  • body — PR description (Markdown supported)
  • head (required) — Source branch name
  • base (required) — Target branch name
  • draft — Create as draft (default: false)

Returns: { data: { number, url, state, title } }

Permission: This tool has riskLevel: 'medium' since it creates external state.

Using Individual GitHub Tool Factories

You can also create GitHub tools individually for custom setups:

typescript
import {
  createSearchFilesTool,
  createSearchCodeTool,
  createGitHubReadFileTool,
  createListBranchesTool,
  createGetPRTool,
  createPRFilesTool,
  createPRCommentsTool,
  createCreatePRTool,
  parseRepository,
} from 'oneringai';

// Create individual tools from a connector
const connector = Connector.get('github');
const searchFiles = createSearchFilesTool(connector);
const readFile = createGitHubReadFileTool(connector);

// Use parseRepository for URL resolution
const { owner, repo } = parseRepository('https://github.com/facebook/react');

Slack Connector Tools

When a Slack connector is configured, ConnectorTools.for('slack') adds ten purpose-built tools alongside the generic authenticated API tool.

typescript
import {
  Agent,
  ConnectorTools,
  createConnectorFromTemplate,
} from 'oneringai';

createConnectorFromTemplate(
  'work-slack',
  'slack',
  'bot-token',
  { apiKey: process.env.SLACK_BOT_TOKEN! },
);

const slackTools = ConnectorTools.for('work-slack');

const agent = Agent.create({
  connector: 'openai-main',
  model: 'gpt-5.6-terra',
  identities: [{ connector: 'work-slack' }],
  tools: slackTools,
});

The names visible to the model are prefixed with the connector name—for example work_slack_post_message—so multiple Slack workspaces can be attached without collisions.

Base tool Purpose
list_channels List channels visible to the connector identity
get_channel_info Fetch channel metadata
set_channel_topic Change a channel topic
get_messages Read recent channel messages
get_thread Read a message thread and replies
post_message Post a message to a channel or thread
search_messages Search Slack messages
get_mentions Retrieve messages mentioning the authenticated user/bot
add_reaction Add an emoji reaction to a message
get_users List workspace users

All operations still pass through ToolManager permissions and the connector's retry, circuit-breaker, metrics, multi-user OAuth, and account-binding logic. Use the generic {connectorName}_api tool for Slack methods not covered by this pack. See the Connector & Tool Catalog for the complete specialized-service matrix.

Microsoft Graph Connector Tools

When a Microsoft connector is configured, ConnectorTools.for('microsoft') automatically includes 11 dedicated tools alongside the generic API tool. These enable email, calendar, meetings, Teams transcript, and OneDrive/SharePoint file workflows.

Quick Start

typescript
import { Connector, ConnectorTools, Services, Agent } from 'oneringai';

// Create a Microsoft connector with OAuth
Connector.create({
  name: 'microsoft',
  serviceType: Services.Microsoft,
  auth: {
    type: 'oauth', flow: 'authorization_code',
    clientId: process.env.MICROSOFT_CLIENT_ID!,
    clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
    redirectUri: 'http://localhost:3000/callback',
    authorizationUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
    tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
    scope: 'https://graph.microsoft.com/.default offline_access',
  },
  baseURL: 'https://graph.microsoft.com/v1.0',
});

// Get all Microsoft tools (generic API + 11 dedicated tools)
const tools = ConnectorTools.for('microsoft');

// Use with an agent
const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools: tools,
});

await agent.run('Draft an email to alice@example.com about the project update');
await agent.run('Schedule a 30-minute Teams meeting with bob@example.com next Tuesday at 2pm');
await agent.run('Find my recent files in OneDrive about the project plan');

Delegated vs Application Mode

Microsoft Graph supports two permission modes:

Mode Path Prefix Use Case
Delegated /me User signs in via OAuth. Tools access the signed-in user's mailbox/calendar.
Application /users/{targetUser} App authenticates as itself (client_credentials). Requires targetUser parameter in each tool call.

All 11 tools accept an optional targetUser parameter (email or user ID). When using delegated auth, this is ignored and /me is used automatically.

create_draft_email

Create a draft email or draft reply in the user's Outlook mailbox.

jsonc
// New draft
{ "to": ["alice@example.com"], "subject": "Project Update", "body": "<p>Here's the latest...</p>" }

// Reply draft
{ "to": ["alice@example.com"], "subject": "Re: Project", "body": "<p>Thanks!</p>", "replyToMessageId": "AAMkAG..." }

Parameters:

  • to (required, string[]) — Recipient email addresses
  • subject (required) — Email subject
  • body (required) — Email body as HTML
  • cc (optional, string[]) — CC email addresses
  • replyToMessageId (optional) — Graph message ID to create a reply draft
  • targetUser (optional) — User ID/email for app-only auth

Returns: { success, draftId, webLink }

send_email

Send an email immediately or reply to an existing message.

jsonc
// Send new email
{ "to": ["bob@example.com"], "subject": "Meeting Notes", "body": "<p>Attached are the notes.</p>" }

// Reply to existing message
{ "to": ["bob@example.com"], "subject": "Re: Meeting Notes", "body": "<p>Thanks!</p>", "replyToMessageId": "AAMkAG..." }

Parameters:

  • to (required, string[]) — Recipient email addresses
  • subject (required) — Email subject
  • body (required) — Email body as HTML
  • cc (optional, string[]) — CC email addresses
  • replyToMessageId (optional) — Graph message ID to reply to
  • replyAll (optional, boolean) — When true with replyToMessageId, replies to all original recipients (To + CC) instead of just the sender. Default: false.
  • targetUser (optional) — User ID/email for app-only auth

Returns: { success }

create_meeting

Create a calendar event on the user's Outlook calendar, optionally with a Teams online meeting link.

jsonc
// Simple meeting
{
  "subject": "Sprint Review",
  "startDateTime": "2026-03-01T14:00:00",
  "endDateTime": "2026-03-01T14:30:00",
  "attendees": ["alice@example.com", "bob@example.com"],
  "isOnlineMeeting": true,
  "timeZone": "America/New_York"
}

Parameters:

  • subject (required) — Meeting title
  • startDateTime (required) — ISO 8601 without timezone (e.g., "2026-03-01T14:00:00")
  • endDateTime (required) — ISO 8601 without timezone
  • attendees (required, string[]) — Attendee email addresses
  • body (optional) — Meeting description as HTML
  • isOnlineMeeting (optional, boolean) — Set true to generate a Teams meeting link
  • location (optional) — Physical location
  • timeZone (optional) — IANA timezone (default: "UTC")
  • targetUser (optional) — User ID/email for app-only auth

Returns: { success, eventId, webLink, onlineMeetingUrl }

edit_meeting

Update an existing Outlook calendar event. Only the fields you provide are changed.

json
{ "eventId": "AAMkAG...", "subject": "Updated Sprint Review", "attendees": ["alice@example.com", "bob@example.com", "charlie@example.com"] }

Parameters:

  • eventId (required) — Graph event ID from create_meeting result
  • subject (optional) — New meeting title
  • startDateTime (optional) — New start time
  • endDateTime (optional) — New end time
  • attendees (optional, string[]) — Full replacement attendee list (not additive)
  • body (optional) — New description as HTML
  • isOnlineMeeting (optional, boolean) — true = add Teams link, false = remove it
  • location (optional) — New location
  • timeZone (optional) — IANA timezone (default: "UTC")
  • targetUser (optional) — User ID/email for app-only auth

Returns: { success, eventId, webLink }

Important: The attendees parameter replaces the entire attendee list — it is not additive.

find_meeting_slots

Find available meeting time slots when all attendees are free.

json
{
  "attendees": ["alice@example.com", "bob@example.com"],
  "startDateTime": "2026-03-01T08:00:00",
  "endDateTime": "2026-03-05T18:00:00",
  "duration": 30,
  "timeZone": "America/New_York",
  "maxResults": 5
}

Parameters:

  • attendees (required, string[]) — Attendee email addresses
  • startDateTime (required) — Search window start (ISO 8601 without timezone)
  • endDateTime (required) — Search window end
  • duration (required, number) — Meeting duration in minutes
  • timeZone (optional) — IANA timezone (default: "UTC")
  • maxResults (optional, number) — Maximum suggestions (default: 5)
  • targetUser (optional) — User ID/email for app-only auth

Returns: { success, slots: [{ start, end, confidence, attendeeAvailability }], emptySuggestionsReason }

get_meeting_transcript

Retrieve the transcript from a Teams online meeting as plain text with speaker labels.

jsonc
// By meeting ID
{ "meetingId": "MSoxMjM0NTY3..." }

// By Teams join URL
{ "meetingId": "https://teams.microsoft.com/l/meetup-join/..." }

Parameters:

  • meetingId (required) — Teams online meeting ID or Teams meeting join URL
  • targetUser (optional) — User ID/email for app-only auth

Returns: { success, transcript, meetingSubject }

Note: Requires OnlineMeetingTranscript.Read.All permission. The meetingId is the Teams online meeting ID (not the calendar event ID). It can be obtained from meeting details or extracted from the Teams join URL.

Required Microsoft Graph Permissions

Tool Delegated Scopes Application Scopes
create_draft_email Mail.ReadWrite Mail.ReadWrite
send_email Mail.Send Mail.Send
create_meeting Calendars.ReadWrite Calendars.ReadWrite
edit_meeting Calendars.ReadWrite Calendars.ReadWrite
find_meeting_slots Calendars.Read Calendars.Read
get_meeting_transcript OnlineMeetingTranscript.Read.All OnlineMeetingTranscript.Read.All

Telegram Connector Tools

6 tools for Telegram Bot API, auto-registered via ConnectorTools.for('telegram') when you create a Telegram connector:

typescript
import { createConnectorFromTemplate, Agent, Vendor, Connector } from 'oneringai';

// Create Telegram connector
createConnectorFromTemplate('my-bot', 'telegram', 'bot-token', {
  apiKey: process.env.TELEGRAM_BOT_TOKEN!,
});

// Agent with Telegram tools
const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  identities: [{ connector: 'my-bot' }],  // Enables Telegram tools
});
Tool Description
telegram_send_message(chat_id, text, parse_mode?) Send a text message with optional HTML/Markdown formatting
telegram_send_photo(chat_id, photo, caption?) Send a photo by URL or file_id with optional caption
telegram_get_updates(offset?, limit?, timeout?) Poll for incoming messages/events (supports long-polling)
telegram_set_webhook(url, drop_pending_updates?) Set or remove webhook for push-based updates (pass url: "" to remove)
telegram_get_me() Get bot info — useful as a connection test
telegram_get_chat(chat_id) Get chat/group/channel details

Key patterns:

  • Token-in-URL: Telegram puts the bot token in the URL path (/bot<TOKEN>/<method>)
  • All responses wrapped in { ok: boolean, result: T }
  • telegramFetch() helper handles auth, JSON encoding, timeout (including long-poll hold time), and error parsing

Twilio Connector Tools

4 tools for SMS and WhatsApp messaging via Twilio, auto-registered via ConnectorTools.for('twilio'):

typescript
import { createConnectorFromTemplate } from 'oneringai';

createConnectorFromTemplate('my-twilio', 'twilio', 'api-key', {
  apiKey: process.env.TWILIO_AUTH_TOKEN!,
  accountId: process.env.TWILIO_ACCOUNT_SID!,
}, {
  vendorOptions: {
    defaultFromNumber: '+15551234567',
    defaultWhatsAppNumber: '+15551234567',
  },
});
Tool Description
send_sms(to, body, from?) Send an SMS. Uses defaultFromNumber from vendor options if from is omitted.
send_whatsapp(to, body, from?, contentSid?) Send a WhatsApp message (freeform or pre-approved template via ContentSid). Uses defaultWhatsAppNumber if from is omitted.
list_messages(to?, from?, dateSentAfter?, dateSentBefore?, channel?, limit?) List/filter messages by phone number, date range, and channel (SMS/WhatsApp/all)
get_message(sid) Get full details of a single message by SID (status, price, errors)

Key patterns:

  • Basic Auth: Twilio uses HTTP Basic Auth (accountSid:authToken base64-encoded) — handled automatically by buildAuthConfig()
  • Form-encoded POST: Twilio API uses application/x-www-form-urlencoded for mutations
  • Phone helpers: normalizePhoneNumber() ensures E.164 format, toWhatsAppNumber() adds whatsapp: prefix
  • Account SID resolution: getAccountSid() resolves from extra.accountId on the connector

Google Workspace Connector Tools

11 tools for Google APIs (Gmail, Calendar, Meet, Drive), auto-registered via ConnectorTools.for('google-api') when a connector with serviceType: 'google-api' exists:

typescript
import { Connector, ConnectorTools, Agent, Vendor } from 'oneringai';

// Create Google OAuth connector
Connector.create({
  name: 'google',
  vendor: Vendor.Google,
  baseURL: 'https://www.googleapis.com',
  auth: {
    type: 'oauth', flow: 'authorization_code',
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    redirectUri: 'http://localhost:3000/callback',
    authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
    tokenUrl: 'https://oauth2.googleapis.com/token',
    scope: 'https://www.googleapis.com/auth/gmail.compose https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/drive.readonly',
  },
  serviceType: 'google-api',
});

// Get all Google tools (generic API + 11 dedicated tools)
const tools = ConnectorTools.for('google');

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  tools,
});

await agent.run('Draft an email to alice@example.com about the project update');
await agent.run('Create a calendar event for next Tuesday at 2pm with Google Meet');
await agent.run('Search my Drive for the Q4 report');
Tool Description Risk
create_draft_email Create a draft email in Gmail, optionally as a reply medium
send_email Send an email or reply via Gmail with HTML body support medium
create_meeting Create Google Calendar event with optional Google Meet link medium
edit_meeting Update an existing Google Calendar event medium
get_meeting Get full details of a single calendar event low
list_meetings List calendar events in a time window low
find_meeting_slots Find available time slots via Google freeBusy API low
get_meeting_transcript Retrieve Google Meet transcript from Drive low
read_file Read a file from Google Drive as markdown (Docs, Sheets, Slides, PDF, images) low
list_files List files/folders in Google Drive low
search_files Full-text search across Google Drive low

Authentication modes:

Mode userId param When to use
OAuth (delegated) User's identity User signs in via Google OAuth. Tools access the signed-in user's Gmail/Calendar/Drive.
Service Account Service account email Server-to-server access. targetUser parameter in tools for domain-wide delegation.

Key patterns:

  • googleFetch() helper handles OAuth and service account auth, JSON encoding, error parsing, and multi-account awareness
  • Google-native file formats (Docs, Sheets, Slides) are exported to markdown/CSV automatically in read_file
  • isServiceAccountAuth() checks connector auth type to determine path prefix behavior
  • All tools accept optional targetUser parameter for service account impersonation

Zoom Connector Tools

3 tools for Zoom meeting management, auto-registered via ConnectorTools.for('zoom'):

typescript
import { createConnectorFromTemplate, ConnectorTools } from 'oneringai';

// OAuth User Token
createConnectorFromTemplate('my-zoom', 'zoom', 'oauth-user', {
  clientId: process.env.ZOOM_CLIENT_ID!,
  redirectUri: 'http://localhost:3000/callback',
});

// Or Server-to-Server OAuth
createConnectorFromTemplate('zoom-s2s', 'zoom', 'server-to-server', {
  clientId: process.env.ZOOM_S2S_CLIENT_ID!,
  clientSecret: process.env.ZOOM_S2S_CLIENT_SECRET!,
  accountId: process.env.ZOOM_ACCOUNT_ID!,
});
Tool Description Risk
zoom_create_meeting Create instant or scheduled Zoom meeting. Returns join URL, start URL, password. medium
zoom_update_meeting Update meeting settings (topic, time, duration, waiting room, join-before-host) medium
zoom_get_transcript Download and parse cloud recording transcript (VTT → structured speaker-attributed text) low

Key patterns:

  • zoomFetch() helper with query param support, error handling, and 204 No Content handling
  • parseMeetingId() extracts meeting ID from URL (https://zoom.us/j/123...) or raw numeric ID
  • parseVTT() parses WebVTT transcript into structured TranscriptEntry[] with speaker, timestamps, and text

Unified Calendar Tool

Cross-provider meeting slot finder that aggregates busy intervals from multiple calendar backends (Google, Microsoft):

typescript
import { tools } from 'oneringai';

// Create providers from existing connectors
const googleProvider = tools.createGoogleCalendarSlotsProvider(googleConnector);
const msftProvider = tools.createMicrosoftCalendarSlotsProvider(msftConnector);

// Unified tool checks all providers in parallel
const tool = tools.createUnifiedFindMeetingSlotsTool([googleProvider, msftProvider]);

const result = await tool.execute({
  attendees: ['alice@gmail.com', 'bob@outlook.com'],
  startDateTime: '2026-04-15T08:00:00',
  endDateTime: '2026-04-15T18:00:00',
  duration: 30,
  timeZone: 'America/New_York',
  maxResults: 5,
  // Optional: route attendees to specific providers
  attendeeMapping: {
    'alice@gmail.com': 'Google',
    'bob@outlook.com': 'Microsoft',
  },
});
// result.slots: MeetingSlotSuggestion[] — times when ALL attendees are free

The ICalendarSlotsProvider interface is extensible — implement it for any calendar backend:

typescript
interface ICalendarSlotsProvider {
  readonly name: string;
  execute(args: GetBusyIntervalsArgs): Promise<GetBusyIntervalsResult>;
}

Multi-Account Connectors

Use multiple accounts per connector (e.g., work + personal Microsoft accounts) with automatic account resolution:

typescript
import { Agent, Connector, Vendor } from 'oneringai';

const agent = Agent.create({
  connector: 'openai',
  model: 'gpt-4.1',
  identities: [
    { connector: 'microsoft', accountId: 'work' },
    { connector: 'microsoft', accountId: 'personal' },
    { connector: 'google', accountId: 'main', toolFilter: ['send_email', 'read_file'] },
  ],
});

How it works:

  1. Each identity generates its own set of tools via ConnectorTools.for(connector, { accountId })
  2. Tools are registered with source metadata (e.g., connector:microsoft:work) for identity filtering
  3. toolFilter (optional) restricts which tools are generated per identity
  4. Account resolution follows a 4-tier priority:
    • Explicit accountId from ConnectorTools.for({ accountId }) (highest)
    • context.accountId already set on ToolContext
    • context.connectorAccounts[connectorName] per-connector binding map
    • undefined — legacy path, no binding

OAuth account management:

typescript
// Re-key a temporary account ID to a stable one (e.g., after discovering email)
await connector.rekeyAccount('user-123', 'temp-abc', 'alice@work.com');

// Remove an account
await connector.removeAccount('user-123', 'personal');

// List all accounts for a user
const accounts = await connector.listAccounts('user-123');

Documentation snapshot · 2026-11-02Suggest a correction ↗

Search documentation

Type to search the v2 documentation.