Skip to main content

ToolRegistry reference

The tool registry holds every tool the agent can invoke, filters by personality toolset, and runs parallel execution with budget enforcement and output reduction.

Source

Interface in packages/types/src/tool.ts (ToolRegistry, ToolFilterOpts). Implementation in packages/core/src/tool-registry.ts (DefaultToolRegistry). Reducer registry in packages/core/src/tool-reducer-registry.ts (DefaultToolResultReducerRegistry).

ToolRegistry

Signature

import type {
Attachment,
Tool,
ToolContext,
ToolFilterOpts,
ToolRegistry,
ToolResult,
} from '@ethosagent/types';
import type { ToolDefinitionLite } from '@ethosagent/types';

export interface ToolRegistry {
register(tool: Tool, opts?: { pluginId?: string }): void;
registerAll(tools: Tool[]): void;
unregister(name: string): void;
get(name: string): Tool | undefined;
getAvailable(): Tool[];
getForToolset(toolset: string): Tool[];
executeParallel(
calls: Array<{ toolCallId: string; name: string; args: unknown }>,
ctx: ToolContext,
allowedTools?: string[],
filterOpts?: ToolFilterOpts,
turnAttachments?: Attachment[],
): Promise<Array<{ toolCallId: string; name: string; result: ToolResult }>>;
toDefinitions(
allowedTools?: string[],
filterOpts?: ToolFilterOpts,
): ToolDefinitionLite[];
}

Methods

MethodSignatureDescription
register(tool: Tool, opts?: { pluginId?: string }): voidRegister a tool. Optional pluginId tags it for per-personality plugin gating.
registerAll(tools: Tool[]): voidRegister multiple tools.
unregister(name: string): voidRemove a tool by name.
get(name: string): Tool | undefinedLookup by name.
getAvailable(): Tool[]All tools where isAvailable() returns true (or is absent).
getForToolset(toolset: string): Tool[]All tools in a given toolset group.
executeParallel(calls, ctx, allowedTools?, filterOpts?, turnAttachments?): Promise<...>Run tool calls concurrently with budget split and reduction. See executeParallel.
toDefinitions(allowedTools?, filterOpts?): ToolDefinitionLite[]Build LLM tool definitions, filtered by personality toolset. See toDefinitions.

DefaultToolRegistry

The concrete implementation in packages/core/src/tool-registry.ts.

Constructor

import type { CapabilityBackends } from '@ethosagent/core';
import type { ToolResultReducerRegistry } from '@ethosagent/types';

const tools = new DefaultToolRegistry(capabilityBackends?, reducerRegistry?);
ParameterTypeDescription
capabilityBackendsCapabilityBackends | undefinedOptional backends for tools that declare capabilities (network, secrets, storage, fs_reach, process, attachments).
reducerRegistryToolResultReducerRegistry | undefinedOptional registry of result reducers. When present, executeParallel applies reducers after execution and before budget trim.

executeParallel

The core method. Runs every requested tool call concurrently via Promise.allSettled. Returns results in input order. Never throws -- failures become { ok: false } results.

Pipeline

  1. Budget split -- perCallBudget = Math.floor(ctx.resultBudgetChars / Math.max(calls.length, 1)). Default total budget: 80,000 chars.
  2. Unknown tool check -- If the tool name is not in the registry: { ok: false, error: 'Unknown tool: ...', code: 'not_available' }.
  3. Allowlist check -- Each call is checked against allowedTools + filterOpts. Built-in tools (not mcp__*, no pluginId) must appear in allowedTools when that list is non-empty. MCP and plugin tools are gated by filterOpts via passesFilter(). Rejected calls get { ok: false, error: '...not permitted...', code: 'not_available' }.
  4. Availability check -- If the tool declares isAvailable() and it returns false: { ok: false, error: '...not currently available', code: 'not_available' }.
  5. Capability backend check -- If the tool declares capabilities that need backends (network, secrets, storage, fs_reach, process, attachments) and no backends are configured: { ok: false, code: 'not_available' }.
  6. Dry-run mode -- If ctx.dryRun, returns a synthetic result without execution.
  7. Per-call budget -- Math.min(perCallBudget, tool.maxResultChars ?? perCallBudget).
  8. Capability resolution -- If the tool declares capabilities and backends are configured, backends are resolved and injected into the tool context.
  9. Execution -- tool.execute(args, ctx).
  10. Reducer pipeline -- After execution, before budget trim. Looks up ToolResultReducer by tool name in the reducer registry. If found, calls safeReduce(reducer, result, { args, turnCount }). Safe: catches reducer errors and returns the original result.
  11. Post-trim -- If the result value exceeds the per-call budget, truncates and appends \n[truncated — N chars total].

toDefinitions

Filters the registry by personality toolset (allowedTools) + MCP/plugin filters (filterOpts). Returns ToolDefinitionLite[] for the LLM request.

Filtering gates (applied in order)

  1. isAvailable() gate -- Tools that declare isAvailable and return false are dropped.
  2. Toolset gate (built-in tools only) -- Built-in tools (name does not start with mcp__, no pluginId) must appear in allowedTools when that list is non-empty. alwaysInclude: true bypasses this gate.
  3. filterOpts gate -- allowedMcpServers filters mcp__<server>__* tools by server name. allowedPlugins filters plugin-tagged tools by pluginId.

Surviving entries are mapped to { name, description, parameters: tool.schema }.

DefaultToolResultReducerRegistry

The reducer registry implementation in packages/core/src/tool-reducer-registry.ts. Provides exact-match lookup of ToolResultReducer by tool name.

Signature

import type { ToolResultReducer, ToolResultReducerRegistry } from '@ethosagent/types';

export class DefaultToolResultReducerRegistry implements ToolResultReducerRegistry {
register(reducer: ToolResultReducer): () => void;
get(toolName: string): ToolResultReducer | undefined;
}

Methods

MethodSignatureDescription
register(reducer: ToolResultReducer): () => voidRegister a reducer. Throws if one is already registered for the same tool name. Returns a cleanup function that unregisters the reducer.
get(toolName: string): ToolResultReducer | undefinedLookup reducer by tool name. Exact match.

Built-in reducers

ReducerTool nameSourceStrategy
Bash reducerterminalextensions/tools-terminal/src/reducers/bash.tsRecognizes git status, test runs, package installs; head+tail fallback for output exceeding 8 KB.
Read-file reducerread_fileextensions/tools-code/src/reducers/read-file.tsPrepends file-size hint, truncates to 200 lines on unconstrained reads (no lineStart/lineEnd).
Kanban-list reducerkanban_listextensions/tools-kanban/src/reducers/kanban-list.tsStatus counts + top 5 open tickets when list exceeds 10 items.

Wiring

How it is assembled at startup in packages/wiring/src/index.ts:

import { DefaultToolRegistry, DefaultToolResultReducerRegistry } from '@ethosagent/core';
import { bashReducer } from '@ethosagent/tools-terminal/reducers/bash';
import { readFileReducer } from '@ethosagent/tools-code/reducers/read-file';
import { kanbanListReducer } from '@ethosagent/tools-kanban/reducers/kanban-list';

const reducerRegistry = new DefaultToolResultReducerRegistry();
reducerRegistry.register(bashReducer);
reducerRegistry.register(readFileReducer);
reducerRegistry.register(kanbanListReducer);
const tools = new DefaultToolRegistry(capabilityBackends, reducerRegistry);

See also