/** * KHAELOR * File: src/tools/registry.ts * Description: ToolDefinition interface and ToolRegistry — schema export for the Anthropic tools parameter. * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import { KhaelorError } from "../shared/index.js"; import type { ParsedInput, ToolJsonSchema } from "./schema.js"; import { validateToolInput } from "./schema.js"; import type { ToolCapabilityHint, ToolContext, ToolName, ToolResult } from "./types.js"; /** * A tool the model can call (TOOL_PROTOCOL §1.2). The JSON schema is the * source of truth for the Anthropic `input_schema`; `capability` is the * coarse hint the executor's permission layer refines into capability * requests before `execute` runs. Tools never check permissions themselves. */ export interface ToolDefinition

{ readonly name: ToolName; /** Model-facing description — the exact text sent in the tools array. */ readonly description: string; /** Flat object schema, ≤5 properties (ADR-8). */ readonly inputSchema: ToolJsonSchema; /** Coarse capability hint for the permission layer. */ readonly capability: ToolCapabilityHint; /** Runs only after approval. Must respect ctx.signal. */ execute(input: P, ctx: ToolContext): Promise; } /** One entry of the Anthropic request's `tools` array. */ export interface AnthropicToolParam { name: string; description: string; input_schema: ToolJsonSchema; } /** Validate a raw tool_use input against a tool's schema (repair prose on failure). */ export function parseToolInput

(tool: ToolDefinition

, input: unknown): ParsedInput

{ return validateToolInput

(tool.name, tool.inputSchema, input); } /** Registry of the tools available to the model (TOOL_PROTOCOL §1.2). */ export class ToolRegistry { private readonly tools = new Map(); register(tool: ToolDefinition): void { if (this.tools.has(tool.name)) { throw new KhaelorError("internal", `Tool "${tool.name}" is already registered.`); } if (Object.keys(tool.inputSchema.properties).length > 5) { throw new KhaelorError("internal", `Tool "${tool.name}" exceeds the 5-parameter limit (ADR-8).`); } this.tools.set(tool.name, tool); } list(): ToolDefinition[] { return [...this.tools.values()]; } get(name: string): ToolDefinition | undefined { return this.tools.get(name as ToolName); } /** `[{name, description, input_schema}]` for the Anthropic request. */ toAnthropicTools(): AnthropicToolParam[] { return this.list().map((tool) => ({ name: tool.name, description: tool.description, input_schema: tool.inputSchema, })); } }