/** * KHAELOR * File: src/tools/schema.ts * Description: JSON-schema subset for tool inputs and the validator that produces repair prose (TOOL_PROTOCOL §1.2). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ /** One tool parameter. The subset of JSON Schema the seven tools need. */ export interface ToolPropertySchema { type: "string" | "integer" | "boolean"; description: string; enum?: string[]; } /** A tool's input_schema — always a flat object with ≤5 properties (ADR-8). */ export interface ToolJsonSchema { type: "object"; properties: Record; required: string[]; } export type ParsedInput

= { ok: true; value: P } | { ok: false; error: string }; function repair(toolName: string, problem: string): string { return `Invalid input for tool "${toolName}": ${problem}. Please rewrite the input so it satisfies the expected schema.`; } /** * Validate a decoded tool_use input against the tool's schema. Failures * return model-facing repair prose (never thrown to the kernel). */ export function validateToolInput

( toolName: string, schema: ToolJsonSchema, input: unknown, ): ParsedInput

{ if (typeof input !== "object" || input === null || Array.isArray(input)) { return { ok: false, error: repair(toolName, "the input must be a JSON object") }; } const record = input as Record; for (const name of schema.required) { if (record[name] === undefined || record[name] === null) { return { ok: false, error: repair(toolName, `parameter "${name}" is required`) }; } } for (const [name, value] of Object.entries(record)) { if (value === undefined || value === null) continue; const prop = schema.properties[name]; if (prop === undefined) continue; // Unknown params are tolerated (lenient decode). if (prop.type === "string" && typeof value !== "string") { return { ok: false, error: repair(toolName, `parameter "${name}" must be a string`) }; } if (prop.type === "boolean" && typeof value !== "boolean") { return { ok: false, error: repair(toolName, `parameter "${name}" must be a boolean`) }; } if (prop.type === "integer" && (typeof value !== "number" || !Number.isInteger(value))) { return { ok: false, error: repair(toolName, `parameter "${name}" must be an integer`) }; } if (prop.enum !== undefined && typeof value === "string" && !prop.enum.includes(value)) { return { ok: false, error: repair( toolName, `parameter "${name}" must be one of ${prop.enum.map((v) => `"${v}"`).join(", ")}`, ), }; } } return { ok: true, value: record as P }; } /** Repair prose for conditionally-required parameters (e.g. process start without command). */ export function missingConditionalParam(toolName: string, param: string, when: string): string { return repair(toolName, `parameter "${param}" is required ${when}`); }