/**
 * Enhanced validation error with structured error information
 */
export class ToolArgumentValidationError extends Error {
    issues;
    toolName;
    constructor(toolName, issues) {
        super(formatMessage(toolName, issues));
        this.name = 'ToolArgumentValidationError';
        this.issues = issues;
        this.toolName = toolName;
    }
}
/**
 * Runtime type guards for tool argument validation
 */
export var TypeGuards;
(function (TypeGuards) {
    /**
     * Type guard for string values
     */
    function isString(value) {
        return typeof value === 'string';
    }
    TypeGuards.isString = isString;
    /**
     * Type guard for number values
     */
    function isNumber(value) {
        return typeof value === 'number' && !Number.isNaN(value);
    }
    TypeGuards.isNumber = isNumber;
    /**
     * Type guard for boolean values
     */
    function isBoolean(value) {
        return typeof value === 'boolean';
    }
    TypeGuards.isBoolean = isBoolean;
    /**
     * Type guard for array values
     */
    function isArray(value) {
        return Array.isArray(value);
    }
    TypeGuards.isArray = isArray;
    /**
     * Type guard for object values
     */
    function isObject(value) {
        return typeof value === 'object' && value !== null && !Array.isArray(value);
    }
    TypeGuards.isObject = isObject;
    /**
     * Type guard for non-null values
     */
    function isNotNull(value) {
        return value !== undefined && value !== null;
    }
    TypeGuards.isNotNull = isNotNull;
    /**
     * Type guard for enum values
     */
    function isEnum(value, enumValues) {
        return TypeGuards.isString(value) && enumValues.includes(value);
    }
    TypeGuards.isEnum = isEnum;
})(TypeGuards || (TypeGuards = {}));
/**
 * Coerce loosely-typed tool arguments into their declared schema types.
 * This makes tools resilient to providers that emit boolean/number values as strings.
 */
export function coerceToolArguments(schema, args) {
    if (!schema || schema.type !== 'object' || !schema.properties) {
        return args;
    }
    const coerced = { ...args };
    for (const [key, definition] of Object.entries(schema.properties)) {
        if (!Object.hasOwn(args, key)) {
            continue;
        }
        const value = args[key];
        if (value === undefined || value === null) {
            continue;
        }
        switch (definition.type) {
            case 'boolean': {
                const normalized = coerceBoolean(value);
                coerced[key] = normalized;
                break;
            }
            case 'number': {
                const normalized = coerceNumber(value);
                coerced[key] = normalized;
                break;
            }
            default:
                break;
        }
    }
    return coerced;
}
function coerceBoolean(value) {
    if (typeof value === 'boolean') {
        return value;
    }
    if (typeof value === 'number') {
        if (value === 1)
            return true;
        if (value === 0)
            return false;
    }
    if (typeof value === 'string') {
        const trimmed = value.trim().toLowerCase();
        if (trimmed === 'true' || trimmed === '1')
            return true;
        if (trimmed === 'false' || trimmed === '0')
            return false;
    }
    return value;
}
function coerceNumber(value) {
    if (typeof value === 'number') {
        // Reject NaN, Infinity, and -Infinity
        if (!Number.isFinite(value)) {
            return value; // Return as-is to fail validation
        }
        return value;
    }
    if (typeof value === 'string' && value.trim().length > 0) {
        const parsed = Number(value);
        // Only accept finite numbers
        if (Number.isFinite(parsed)) {
            return parsed;
        }
    }
    return value;
}
export function validateToolArguments(toolName, schema, args) {
    if (!schema || schema.type !== 'object') {
        return;
    }
    const errors = [];
    const properties = schema.properties ?? {};
    const required = Array.isArray(schema.required) ? schema.required : [];
    for (const property of required) {
        if (!hasArgument(args, property)) {
            errors.push(`Missing required property "${property}".`);
        }
    }
    for (const [key, value] of Object.entries(args)) {
        const definition = properties[key];
        if (!definition) {
            // Silently ignore unknown properties - models sometimes hallucinate extra params
            // This is more lenient than strict JSON schema validation but prevents errors
            // when models pass extra parameters that don't affect tool behavior
            continue;
        }
        validateSchemaProperty(definition, value, key, errors);
    }
    if (errors.length) {
        throw new ToolArgumentValidationError(toolName, errors);
    }
}
function validateSchemaProperty(definition, value, path, errors) {
    switch (definition.type) {
        case 'string': {
            if (typeof value !== 'string') {
                errors.push(`Argument "${path}" must be a string.`);
                return;
            }
            if (definition.enum && !definition.enum.includes(value)) {
                errors.push(`Argument "${path}" must be one of: ${definition.enum.map((entry) => `"${entry}"`).join(', ')}.`);
            }
            if (typeof definition.minLength === 'number' && value.length < definition.minLength) {
                errors.push(`Argument "${path}" must be at least ${definition.minLength} character${definition.minLength === 1 ? '' : 's'} long.`);
            }
            return;
        }
        case 'number': {
            if (typeof value !== 'number' || !Number.isFinite(value)) {
                errors.push(`Argument "${path}" must be a finite number.`);
                return;
            }
            // Validate minimum constraint
            if (typeof definition.minimum === 'number' && value < definition.minimum) {
                errors.push(`Argument "${path}" must be at least ${definition.minimum}.`);
            }
            // Validate maximum constraint
            if (typeof definition.maximum === 'number' && value > definition.maximum) {
                errors.push(`Argument "${path}" must be at most ${definition.maximum}.`);
            }
            return;
        }
        case 'boolean': {
            if (typeof value !== 'boolean') {
                errors.push(`Argument "${path}" must be a boolean.`);
            }
            return;
        }
        case 'array': {
            if (!Array.isArray(value)) {
                errors.push(`Argument "${path}" must be an array.`);
                return;
            }
            validateArrayItems(definition, value, path, errors);
            return;
        }
        default:
            return;
    }
}
function validateArrayItems(definition, value, path, errors) {
    const itemSchema = definition.items;
    if (!itemSchema) {
        return;
    }
    for (let index = 0; index < value.length; index += 1) {
        const entry = value[index];
        validateSchemaProperty(itemSchema, entry, `${path}[${index}]`, errors);
    }
}
function hasArgument(args, key) {
    if (!Object.hasOwn(args, key)) {
        return false;
    }
    const value = args[key];
    return value !== undefined && value !== null;
}
function formatMessage(toolName, issues) {
    const detail = issues.join(' ');
    return `Invalid arguments for "${toolName}": ${detail}`;
}
//# sourceMappingURL=schemaValidator.js.map