50 lines
1.3 KiB
TypeScript
50 lines
1.3 KiB
TypeScript
/**
|
|
* Runtime validation for tool arguments using ajv.
|
|
*
|
|
* Ported from pi-mono packages/ai/src/utils/validation.ts pattern:
|
|
* - Singleton ajv instance with coercion
|
|
* - Graceful degradation if ajv fails to initialise (CSP)
|
|
*/
|
|
|
|
import Ajv from 'ajv';
|
|
import addFormats from 'ajv-formats';
|
|
import type { TSchema } from '@sinclair/typebox';
|
|
|
|
let ajvInstance: Ajv | null = null;
|
|
|
|
function getAjv(): Ajv | null {
|
|
if (ajvInstance) return ajvInstance;
|
|
|
|
try {
|
|
ajvInstance = new Ajv({ allErrors: true, strict: false, coerceTypes: true });
|
|
addFormats(ajvInstance);
|
|
return ajvInstance;
|
|
} catch {
|
|
// Graceful degradation — skip validation if ajv cannot initialise
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate and coerce tool arguments against a TypeBox / JSON Schema.
|
|
* Throws a formatted error on validation failure.
|
|
* Gracefully skips validation when ajv is unavailable (e.g. CSP).
|
|
*/
|
|
export function validateToolArguments(
|
|
toolName: string,
|
|
schema: TSchema,
|
|
args: unknown,
|
|
): void {
|
|
const ajv = getAjv();
|
|
if (!ajv) return;
|
|
|
|
const validate = ajv.compile(schema);
|
|
const valid = validate(args);
|
|
if (valid) return;
|
|
|
|
const errors = validate.errors
|
|
?.map((e) => `${e.instancePath || '/'} ${e.message}`)
|
|
.join('; ');
|
|
throw new Error(`Tool "${toolName}" received invalid arguments: ${errors}`);
|
|
}
|