UNPKG

@dexwox-labs/a2a-core

Version:

Core types, validation and telemetry for Google's Agent-to-Agent (A2A) protocol - shared foundation for client and server implementations

448 lines 15.3 kB
"use strict"; /** * @module Validators * @description Schema validation utilities for A2A protocol types * * This module provides schema definitions and validation functions for the A2A protocol. * It uses Zod for runtime type validation and provides helper functions for validating * various protocol objects. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.schemas = exports.isPushNotificationConfig = exports.isAgentCard = exports.isTask = exports.isMessage = exports.validateDiscoverResponse = exports.validateDiscoverRequest = exports.validatePushNotificationConfig = exports.validateAgentCard = exports.validateTask = exports.validateMessage = void 0; exports.formatValidationError = formatValidationError; exports.validate = validate; const zod_1 = require("zod"); const a2a_protocol_1 = require("../types/a2a-protocol"); // Extended schemas with additional validation const TaskTransitionSchema = zod_1.z.object({ from: a2a_protocol_1.TaskStateSchema, to: a2a_protocol_1.TaskStateSchema, timestamp: zod_1.z.string().datetime(), reason: zod_1.z.string().optional(), }); // Define the message part schemas individually const TextMessagePartSchema = zod_1.z.object({ type: zod_1.z.literal('text'), content: zod_1.z.string(), format: zod_1.z.enum(['plain', 'markdown']).default('plain'), }); const FileMessagePartSchema = zod_1.z.object({ type: zod_1.z.literal('file'), content: zod_1.z.union([zod_1.z.string(), zod_1.z.instanceof(Uint8Array)]), mimeType: zod_1.z.string(), name: zod_1.z.string(), size: zod_1.z.number().optional(), }); const DataMessagePartSchema = zod_1.z.object({ type: zod_1.z.literal('data'), content: zod_1.z.record(zod_1.z.any()), schema: zod_1.z.string().optional(), }); const HeartbeatMessagePartSchema = zod_1.z.object({ type: zod_1.z.literal('heartbeat'), content: zod_1.z.string(), format: zod_1.z.literal('plain').default('plain'), }); // Create the union type const StrictMessagePartSchema = zod_1.z.union([ TextMessagePartSchema, FileMessagePartSchema, DataMessagePartSchema, HeartbeatMessagePartSchema, ]); const MessageSchema = zod_1.z.object({ parts: zod_1.z.array(StrictMessagePartSchema).min(1, 'At least one message part is required'), taskId: zod_1.z.string().uuid().optional(), contextId: zod_1.z.string().uuid().optional(), }); const MessageSendConfigurationSchema = zod_1.z.object({ priority: zod_1.z.number().int().min(0).max(100).optional(), timeout: zod_1.z.number().int().positive().optional(), metadata: zod_1.z.record(zod_1.z.unknown()).optional(), }); const TaskSchema = zod_1.z.object({ id: zod_1.z.string().uuid(), name: zod_1.z.string().min(1, 'Task name is required'), description: zod_1.z.string().optional(), status: a2a_protocol_1.TaskStateSchema, agentId: zod_1.z.string().optional(), parts: zod_1.z.array(StrictMessagePartSchema).optional(), expectedParts: zod_1.z.number().int().positive().optional(), artifacts: zod_1.z.array(a2a_protocol_1.ArtifactSchema).optional(), transitions: zod_1.z.array(TaskTransitionSchema).optional(), createdAt: zod_1.z.string().datetime(), updatedAt: zod_1.z.string().datetime(), error: a2a_protocol_1.A2AErrorSchema.optional(), metadata: zod_1.z.record(zod_1.z.unknown()).optional(), contextId: zod_1.z.string().uuid().optional(), inputSchema: zod_1.z.record(zod_1.z.any()).optional(), outputSchema: zod_1.z.record(zod_1.z.any()).optional(), input: zod_1.z.record(zod_1.z.any()).optional(), output: zod_1.z.record(zod_1.z.any()).optional(), }); const AgentCardSchema = zod_1.z.object({ id: zod_1.z.string().uuid(), name: zod_1.z.string().min(1, 'Agent name is required'), capabilities: zod_1.z.array(zod_1.z.string().min(1, 'Capability cannot be empty')), endpoint: zod_1.z.string().url('Endpoint must be a valid URL'), metadata: zod_1.z.record(zod_1.z.unknown()).optional(), }); const PushNotificationConfigSchema = zod_1.z.object({ enabled: zod_1.z.boolean(), endpoint: zod_1.z.string().url('Endpoint must be a valid URL').optional(), authToken: zod_1.z.string().optional(), events: zod_1.z.array(zod_1.z.string().min(1, 'Event name cannot be empty')), metadata: zod_1.z.record(zod_1.z.unknown()).optional(), }); const DiscoverRequestSchema = zod_1.z.object({ id: zod_1.z.string().min(1, 'ID is required'), method: zod_1.z.literal('discover'), params: zod_1.z.record(zod_1.z.unknown()).optional(), jsonrpc: zod_1.z.literal('2.0').optional(), }); const DiscoverResponseSchema = zod_1.z.object({ id: zod_1.z.string(), result: zod_1.z.array(AgentCardSchema), error: a2a_protocol_1.A2AErrorSchema.optional(), }); /** * Validation functions for A2A protocol objects * * These functions validate objects against their respective schemas and return * a SafeParseReturnType that includes either the validated data or validation errors. */ /** * Validates a message object against the Message schema * * @param data - The data to validate * @returns A SafeParseReturnType containing either the validated Message or validation errors * * @example * ```typescript * const result = validateMessage({ * parts: [{ type: 'text', content: 'Hello, world!' }] * }); * * if (result.success) { * // Use the validated message * console.log('Valid message:', result.data); * } else { * // Handle validation errors * console.error('Invalid message:', result.error); * } * ``` */ const validateMessage = (data) => MessageSchema.safeParse(data); exports.validateMessage = validateMessage; /** * Validates a task object against the Task schema * * @param data - The data to validate * @returns A SafeParseReturnType containing either the validated Task or validation errors * * @example * ```typescript * const result = validateTask({ * id: '123e4567-e89b-12d3-a456-426614174000', * name: 'Process Data', * status: 'submitted', * createdAt: new Date().toISOString(), * updatedAt: new Date().toISOString() * }); * * if (result.success) { * // Use the validated task * console.log('Valid task:', result.data); * } else { * // Handle validation errors * console.error('Invalid task:', result.error); * } * ``` */ const validateTask = (data) => TaskSchema.safeParse(data); exports.validateTask = validateTask; /** * Validates an agent card object against the AgentCard schema * * @param data - The data to validate * @returns A SafeParseReturnType containing either the validated AgentCard or validation errors * * @example * ```typescript * const result = validateAgentCard({ * id: '123e4567-e89b-12d3-a456-426614174000', * name: 'Weather Agent', * capabilities: ['weather-forecasting', 'location-search'], * endpoint: 'https://example.com/agents/weather' * }); * * if (result.success) { * // Use the validated agent card * console.log('Valid agent card:', result.data); * } else { * // Handle validation errors * console.error('Invalid agent card:', result.error); * } * ``` */ const validateAgentCard = (data) => AgentCardSchema.safeParse(data); exports.validateAgentCard = validateAgentCard; /** * Validates a push notification configuration against the PushNotificationConfig schema * * @param data - The data to validate * @returns A SafeParseReturnType containing either the validated PushNotificationConfig or validation errors * * @example * ```typescript * const result = validatePushNotificationConfig({ * enabled: true, * endpoint: 'https://example.com/webhooks/a2a', * authToken: 'secret-token', * events: ['taskCompleted', 'taskFailed'] * }); * * if (result.success) { * // Use the validated config * console.log('Valid push config:', result.data); * } else { * // Handle validation errors * console.error('Invalid push config:', result.error); * } * ``` */ const validatePushNotificationConfig = (data) => PushNotificationConfigSchema.safeParse(data); exports.validatePushNotificationConfig = validatePushNotificationConfig; /** * Validates a discover request against the DiscoverRequest schema * * @param data - The data to validate * @returns A SafeParseReturnType containing either the validated DiscoverRequest or validation errors * * @example * ```typescript * const result = validateDiscoverRequest({ * id: '1', * method: 'discover', * params: { capability: 'weather-forecasting' }, * jsonrpc: '2.0' * }); * * if (result.success) { * // Use the validated request * console.log('Valid discover request:', result.data); * } else { * // Handle validation errors * console.error('Invalid discover request:', result.error); * } * ``` */ const validateDiscoverRequest = (data) => DiscoverRequestSchema.safeParse(data); exports.validateDiscoverRequest = validateDiscoverRequest; /** * Validates a discover response against the DiscoverResponse schema * * @param data - The data to validate * @returns A SafeParseReturnType containing either the validated DiscoverResponse or validation errors * * @example * ```typescript * const result = validateDiscoverResponse({ * id: '1', * result: [ * { * id: '123e4567-e89b-12d3-a456-426614174000', * name: 'Weather Agent', * capabilities: ['weather-forecasting'], * endpoint: 'https://example.com/agents/weather' * } * ] * }); * * if (result.success) { * // Use the validated response * console.log('Valid discover response:', result.data); * } else { * // Handle validation errors * console.error('Invalid discover response:', result.error); * } * ``` */ const validateDiscoverResponse = (data) => DiscoverResponseSchema.safeParse(data); exports.validateDiscoverResponse = validateDiscoverResponse; /** * Type guards for A2A protocol objects * * These functions check if an object conforms to a specific schema and narrow * its type if it does. They return a boolean indicating whether the object is * of the specified type. */ /** * Checks if the provided data is a valid Message * * @param data - The data to check * @returns True if the data is a valid Message, false otherwise * * @example * ```typescript * const data = { parts: [{ type: 'text', content: 'Hello' }] }; * * if (isMessage(data)) { * // TypeScript now knows that data is a Message * console.log('Message parts:', data.parts.length); * } * ``` */ const isMessage = (data) => MessageSchema.safeParse(data).success; exports.isMessage = isMessage; /** * Checks if the provided data is a valid Task * * @param data - The data to check * @returns True if the data is a valid Task, false otherwise * * @example * ```typescript * const data = getTaskFromSomewhere(); * * if (isTask(data)) { * // TypeScript now knows that data is a Task * console.log('Task status:', data.status); * } * ``` */ const isTask = (data) => TaskSchema.safeParse(data).success; exports.isTask = isTask; /** * Checks if the provided data is a valid AgentCard * * @param data - The data to check * @returns True if the data is a valid AgentCard, false otherwise * * @example * ```typescript * const data = getAgentFromSomewhere(); * * if (isAgentCard(data)) { * // TypeScript now knows that data is an AgentCard * console.log('Agent capabilities:', data.capabilities); * } * ``` */ const isAgentCard = (data) => AgentCardSchema.safeParse(data).success; exports.isAgentCard = isAgentCard; /** * Checks if the provided data is a valid PushNotificationConfig * * @param data - The data to check * @returns True if the data is a valid PushNotificationConfig, false otherwise * * @example * ```typescript * const data = getConfigFromSomewhere(); * * if (isPushNotificationConfig(data)) { * // TypeScript now knows that data is a PushNotificationConfig * console.log('Push notifications enabled:', data.enabled); * } * ``` */ const isPushNotificationConfig = (data) => PushNotificationConfigSchema.safeParse(data).success; exports.isPushNotificationConfig = isPushNotificationConfig; /** * Formats a Zod validation error into a human-readable string * * This function takes a Zod error object and converts it into a string * representation, with each issue formatted as "path: message" and joined * with semicolons. * * @param error - The Zod error to format * @returns A formatted error string * * @example * ```typescript * const result = validateTask(invalidTask); * * if (!result.success) { * const errorMessage = formatValidationError(result.error); * console.error('Validation failed:', errorMessage); * // Example output: "name: Required; status: Invalid enum value" * } * ``` */ function formatValidationError(error) { return error.issues .map(issue => { const path = issue.path.join('.'); return path ? `${path}: ${issue.message}` : issue.message; }) .join('; '); } /** * Decorator for validating method parameters against a schema * * This decorator can be applied to methods to automatically validate their * first parameter against the provided schema. If validation fails, an error * is thrown with details about the validation issues. * * @param schema - The Zod schema to validate against * @returns A method decorator function * * @example * ```typescript * class MessageService { * @validate(MessageSchema) * sendMessage(message: Message) { * // This code only runs if message passes validation * console.log('Sending message:', message); * } * } * * const service = new MessageService(); * * // This will throw an error because the message is invalid * service.sendMessage({ invalid: 'data' }); * ``` */ function validate(schema) { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args) { const result = schema.safeParse(args[0]); if (!result.success) { throw new Error(`Validation failed: ${formatValidationError(result.error)}`); } return originalMethod.apply(this, args); }; return descriptor; }; } /** * Collection of all schema definitions for A2A protocol objects * * This object provides access to all the Zod schemas defined in this module, * allowing them to be used directly for validation or type inference. * * @example * ```typescript * // Use a schema directly for validation * const result = schemas.Message.safeParse(data); * * // Create a type from a schema * type MessageType = z.infer<typeof schemas.Message>; * ``` */ exports.schemas = { Message: MessageSchema, Task: TaskSchema, AgentCard: AgentCardSchema, PushNotificationConfig: PushNotificationConfigSchema, DiscoverRequest: DiscoverRequestSchema, DiscoverResponse: DiscoverResponseSchema, MessagePart: a2a_protocol_1.MessagePartSchema, Artifact: a2a_protocol_1.ArtifactSchema, A2AError: a2a_protocol_1.A2AErrorSchema, TaskTransition: TaskTransitionSchema, MessageSendConfiguration: MessageSendConfigurationSchema, }; //# sourceMappingURL=validators.js.map