spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: src/tools/schema.ts4 * Description: JSON-schema subset for tool inputs and the validator that produces repair prose (TOOL_PROTOCOL §1.2).5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910/** One tool parameter. The subset of JSON Schema the seven tools need. */11export interface ToolPropertySchema {12 type: "string" | "integer" | "boolean";13 description: string;14 enum?: string[];15}1617/** A tool's input_schema — always a flat object with ≤5 properties (ADR-8). */18export interface ToolJsonSchema {19 type: "object";20 properties: Record<string, ToolPropertySchema>;21 required: string[];22}2324export type ParsedInput<P> = { ok: true; value: P } | { ok: false; error: string };2526function repair(toolName: string, problem: string): string {27 return `Invalid input for tool "${toolName}": ${problem}. Please rewrite the input so it satisfies the expected schema.`;28}2930/**31 * Validate a decoded tool_use input against the tool's schema. Failures32 * return model-facing repair prose (never thrown to the kernel).33 */34export function validateToolInput<P>(35 toolName: string,36 schema: ToolJsonSchema,37 input: unknown,38): ParsedInput<P> {39 if (typeof input !== "object" || input === null || Array.isArray(input)) {40 return { ok: false, error: repair(toolName, "the input must be a JSON object") };41 }42 const record = input as Record<string, unknown>;4344 for (const name of schema.required) {45 if (record[name] === undefined || record[name] === null) {46 return { ok: false, error: repair(toolName, `parameter "${name}" is required`) };47 }48 }4950 for (const [name, value] of Object.entries(record)) {51 if (value === undefined || value === null) continue;52 const prop = schema.properties[name];53 if (prop === undefined) continue; // Unknown params are tolerated (lenient decode).54 if (prop.type === "string" && typeof value !== "string") {55 return { ok: false, error: repair(toolName, `parameter "${name}" must be a string`) };56 }57 if (prop.type === "boolean" && typeof value !== "boolean") {58 return { ok: false, error: repair(toolName, `parameter "${name}" must be a boolean`) };59 }60 if (prop.type === "integer" && (typeof value !== "number" || !Number.isInteger(value))) {61 return { ok: false, error: repair(toolName, `parameter "${name}" must be an integer`) };62 }63 if (prop.enum !== undefined && typeof value === "string" && !prop.enum.includes(value)) {64 return {65 ok: false,66 error: repair(67 toolName,68 `parameter "${name}" must be one of ${prop.enum.map((v) => `"${v}"`).join(", ")}`,69 ),70 };71 }72 }7374 return { ok: true, value: record as P };75}7677/** Repair prose for conditionally-required parameters (e.g. process start without command). */78export function missingConditionalParam(toolName: string, param: string, when: string): string {79 return repair(toolName, `parameter "${param}" is required ${when}`);80}81