UNPKG

semnet-snap-protocol

Version:

TypeScript reference implementation of the SNAP Protocol v1.1 - Agent Internet Edition

436 lines 17 kB
import { z } from 'zod'; // ========================================== // User Identity // ========================================== export const UserIDSchema = z.object({ id: z.string().regex(/^snap:user:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/), publicKey: z.string(), name: z.string().optional(), metadata: z.record(z.any()).optional() }); // ========================================== // Agent Identity // ========================================== export const AgentIDSchema = z.object({ id: z.string().regex(/^snap:agent:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/), publicKey: z.string().optional(), registry: z.string().url().optional() }); // ========================================== // Message Parts // ========================================== export const TextPartSchema = z.object({ type: z.literal('text'), content: z.string(), encoding: z.enum(['utf-8', 'base64']).optional(), metadata: z.object({ format: z.enum(['plain', 'markdown', 'html']).optional(), language: z.string().optional(), }).catchall(z.any()).optional() }); export const DataPartSchema = z.object({ type: z.literal('data'), content: z.record(z.any()), schema: z.record(z.any()).optional(), metadata: z.object({ format: z.enum(['json', 'xml', 'yaml']).optional(), encoding: z.string().optional(), }).catchall(z.any()).optional() }); export const FilePartSchema = z.object({ type: z.literal('file'), content: z.object({ uri: z.string().url().optional(), bytes: z.string().optional(), name: z.string(), mimeType: z.string(), size: z.number().optional(), hash: z.string().optional() }).refine(data => data.uri || data.bytes, { message: "Either uri or bytes must be provided" }), metadata: z.object({ description: z.string().optional(), }).catchall(z.any()).optional() }); export const ImagePartSchema = z.object({ type: z.literal('image'), content: z.object({ uri: z.string().url().optional(), bytes: z.string().optional(), mimeType: z.enum(['image/jpeg', 'image/png', 'image/gif', 'image/webp']), width: z.number().optional(), height: z.number().optional(), alt: z.string().optional() }).refine(data => data.uri || data.bytes, { message: "Either uri or bytes must be provided" }), metadata: z.object({ caption: z.string().optional(), }).catchall(z.any()).optional() }); export const AudioPartSchema = z.object({ type: z.literal('audio'), content: z.object({ uri: z.string().url().optional(), bytes: z.string().optional(), mimeType: z.enum(['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/webm']), duration: z.number().optional(), sampleRate: z.number().optional() }).refine(data => data.uri || data.bytes, { message: "Either uri or bytes must be provided" }), metadata: z.object({ title: z.string().optional(), artist: z.string().optional(), }).catchall(z.any()).optional() }); export const VideoPartSchema = z.object({ type: z.literal('video'), content: z.object({ uri: z.string().url().optional(), bytes: z.string().optional(), mimeType: z.enum(['video/mp4', 'video/webm', 'video/quicktime']), duration: z.number().optional(), width: z.number().optional(), height: z.number().optional(), frameRate: z.number().optional() }).refine(data => data.uri || data.bytes, { message: "Either uri or bytes must be provided" }), metadata: z.object({ title: z.string().optional(), description: z.string().optional(), }).catchall(z.any()).optional() }); export const PartSchema = z.discriminatedUnion('type', [ TextPartSchema, DataPartSchema, FilePartSchema, ImagePartSchema, AudioPartSchema, VideoPartSchema ]); // ========================================== // Payment // ========================================== export const PaymentSchema = z.object({ amount: z.number().positive(), currency: z.literal('SEMNET'), from: z.union([AgentIDSchema, UserIDSchema]), to: z.union([AgentIDSchema, UserIDSchema]), reference: z.string().optional(), memo: z.string().optional(), status: z.enum(['pending', 'authorized', 'executed', 'failed']).optional() }); // ========================================== // Core Message // ========================================== export const SNAPMessageSchema = z.object({ id: z.string(), version: z.enum(['1.0', '1.1']).default('1.1'), from: z.union([AgentIDSchema, UserIDSchema]), to: z.union([AgentIDSchema, UserIDSchema]).optional(), timestamp: z.string().datetime(), parts: z.array(PartSchema).min(1), context: z.string().optional(), payment: PaymentSchema.optional(), metadata: z.record(z.any()).optional(), signature: z.string().optional() }); // ========================================== // JSON-RPC 2.0 Wrapper // ========================================== export const JSONRPCIdSchema = z.union([z.string(), z.number(), z.null()]); export const JSONRPCRequestSchema = z.object({ jsonrpc: z.literal('2.0'), method: z.string(), params: z.record(z.any()).optional(), id: JSONRPCIdSchema }); export const JSONRPCErrorSchema = z.object({ code: z.number(), message: z.string(), data: z.any().optional() }); export const JSONRPCResponseSchema = z.object({ jsonrpc: z.literal('2.0'), result: z.any().optional(), error: JSONRPCErrorSchema.optional(), id: JSONRPCIdSchema }).refine(data => !!(data.result !== undefined) !== !!(data.error !== undefined), { message: "Response must have either result or error, not both" }); // ========================================== // Error Codes // ========================================== export var SNAPErrorCode; (function (SNAPErrorCode) { // JSON-RPC 2.0 Standard Errors SNAPErrorCode[SNAPErrorCode["PARSE_ERROR"] = -32700] = "PARSE_ERROR"; SNAPErrorCode[SNAPErrorCode["INVALID_REQUEST"] = -32600] = "INVALID_REQUEST"; SNAPErrorCode[SNAPErrorCode["METHOD_NOT_FOUND"] = -32601] = "METHOD_NOT_FOUND"; SNAPErrorCode[SNAPErrorCode["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS"; SNAPErrorCode[SNAPErrorCode["INTERNAL_ERROR"] = -32603] = "INTERNAL_ERROR"; // SNAP-specific Errors (-32000 to -32099) SNAPErrorCode[SNAPErrorCode["AGENT_NOT_FOUND"] = -32001] = "AGENT_NOT_FOUND"; SNAPErrorCode[SNAPErrorCode["INVALID_SIGNATURE"] = -32002] = "INVALID_SIGNATURE"; SNAPErrorCode[SNAPErrorCode["PAYMENT_REQUIRED"] = -32003] = "PAYMENT_REQUIRED"; SNAPErrorCode[SNAPErrorCode["INSUFFICIENT_FUNDS"] = -32004] = "INSUFFICIENT_FUNDS"; SNAPErrorCode[SNAPErrorCode["UNSUPPORTED_CONTENT_TYPE"] = -32005] = "UNSUPPORTED_CONTENT_TYPE"; SNAPErrorCode[SNAPErrorCode["RATE_LIMITED"] = -32006] = "RATE_LIMITED"; SNAPErrorCode[SNAPErrorCode["AGENT_UNAVAILABLE"] = -32007] = "AGENT_UNAVAILABLE"; SNAPErrorCode[SNAPErrorCode["CONTEXT_NOT_FOUND"] = -32008] = "CONTEXT_NOT_FOUND"; SNAPErrorCode[SNAPErrorCode["INVALID_AGENT_ID"] = -32009] = "INVALID_AGENT_ID"; SNAPErrorCode[SNAPErrorCode["REGISTRY_ERROR"] = -32010] = "REGISTRY_ERROR"; })(SNAPErrorCode || (SNAPErrorCode = {})); // ========================================== // Account Linking (Optional) // ========================================== export const AccountLinkRequestSchema = z.object({ userId: z.string().regex(/^snap:user:[0-9a-f-]{36}$/), agentId: z.string().regex(/^snap:agent:[0-9a-f-]{36}$/), serviceName: z.string(), linkingUrl: z.string().url(), expiresAt: z.string().datetime().optional() }); export const AccountLinkStatusSchema = z.object({ userId: z.string(), agentId: z.string(), serviceName: z.string(), status: z.enum(['pending', 'linked', 'expired', 'revoked']), linkedAt: z.string().datetime().optional(), expiresAt: z.string().datetime().optional() }); // ========================================== // Agent Discovery // ========================================== export const AgentSkillSchema = z.object({ id: z.string(), name: z.string(), description: z.string(), inputSchema: z.record(z.any()).optional(), outputSchema: z.record(z.any()).optional(), examples: z.array(z.string()).optional(), pricing: z.object({ model: z.enum(['free', 'per-request', 'per-minute', 'subscription']), cost: z.number().optional(), currency: z.literal('SEMNET').optional() }).optional() }); export const AgentCapabilitiesSchema = z.object({ streaming: z.boolean().default(false), tasks: z.boolean().default(false), payments: z.boolean().default(false), files: z.boolean().default(true), maxFileSize: z.number().optional(), supportedMimeTypes: z.array(z.string()).optional() }); // Rate limiting schema (optional) export const RateLimitSchema = z.object({ requests: z.number().positive(), window: z.number().positive(), // window in seconds costLimit: z.number().positive().optional(), // max SEMNET credits per window strategy: z.enum(['fixed-window', 'sliding-window']).default('fixed-window') }); export const AgentCardSchema = z.object({ identity: AgentIDSchema, name: z.string(), description: z.string(), // Optimized for vector search - comprehensive description keywords: z.array(z.string()), // For vector search optimization capabilities: z.array(z.string()), // Simple capability list like ["email.send", "email.read"] version: z.string(), endpoint: z.string().url(), skills: z.array(AgentSkillSchema).optional(), // Made optional for simpler agents technicalCapabilities: AgentCapabilitiesSchema.optional(), // Renamed for clarity pricing: z.object({ model: z.enum(['free', 'per-request', 'per-minute', 'subscription']), cost: z.number().optional(), currency: z.literal('SEMNET').optional() }).optional(), rateLimit: RateLimitSchema.optional(), // Optional rate limiting requiresAccountLink: z.boolean().default(false), // Does this agent need account linking? metadata: z.object({ category: z.string().optional(), tags: z.array(z.string()).optional(), author: z.string().optional(), homepage: z.string().url().optional(), documentation: z.string().url().optional() }).optional() }); // ========================================== // Task System // ========================================== export var TaskStatus; (function (TaskStatus) { TaskStatus["QUEUED"] = "queued"; TaskStatus["PROCESSING"] = "processing"; TaskStatus["COMPLETED"] = "completed"; TaskStatus["FAILED"] = "failed"; TaskStatus["CANCELLED"] = "cancelled"; })(TaskStatus || (TaskStatus = {})); export const TaskSchema = z.object({ id: z.string(), status: z.nativeEnum(TaskStatus), progress: z.number().min(0).max(1).optional(), // 0.0 to 1.0 message: z.string().optional(), // Human readable status result: SNAPMessageSchema.optional(), // Final result when complete error: z.string().optional(), // Error message if failed createdAt: z.string().datetime(), updatedAt: z.string().datetime(), estimatedCompletion: z.string().datetime().optional(), metadata: z.record(z.any()).optional() }); export const TaskCreateRequestSchema = z.object({ message: SNAPMessageSchema, callback: z.string().url().optional(), // Webhook URL for status updates timeout: z.number().optional(), // Timeout in seconds priority: z.enum(['low', 'normal', 'high']).default('normal'), metadata: z.record(z.any()).optional() }); export const TaskUpdateSchema = z.object({ status: z.nativeEnum(TaskStatus).optional(), progress: z.number().min(0).max(1).optional(), message: z.string().optional(), result: SNAPMessageSchema.optional(), error: z.string().optional(), estimatedCompletion: z.string().datetime().optional(), metadata: z.record(z.any()).optional() }); // ========================================== // Streaming System // ========================================== export var StreamEventType; (function (StreamEventType) { StreamEventType["MESSAGE"] = "message"; StreamEventType["STATUS"] = "status"; StreamEventType["PROGRESS"] = "progress"; StreamEventType["ERROR"] = "error"; StreamEventType["COMPLETE"] = "complete"; StreamEventType["CONVERSATION"] = "conversation"; })(StreamEventType || (StreamEventType = {})); // Base stream event schema export const BaseStreamEventSchema = z.object({ id: z.string(), timestamp: z.string().datetime(), metadata: z.record(z.any()).optional() }); // Status stream event - shows what agent is doing export const StatusStreamEventSchema = BaseStreamEventSchema.extend({ type: z.literal('status'), data: z.object({ streamId: z.string(), status: z.enum(['active', 'paused', 'cancelled']), currentAction: z.string(), // "Searching for flights..." progress: z.number().min(0).max(1).optional() // 0.5 = 50% done }) }); // Conversation stream event - shows agent-to-agent messages export const ConversationStreamEventSchema = BaseStreamEventSchema.extend({ type: z.literal('conversation'), data: z.object({ streamId: z.string(), direction: z.enum(['outgoing', 'incoming']), // from user's perspective from: z.union([AgentIDSchema, UserIDSchema]), to: z.union([AgentIDSchema, UserIDSchema]), message: SNAPMessageSchema, // The actual SNAP message latency: z.number().optional(), // Response time in ms cost: z.number().optional() // Cost of this interaction }) }); // Progress stream event export const ProgressStreamEventSchema = BaseStreamEventSchema.extend({ type: z.literal('progress'), data: z.object({ streamId: z.string(), progress: z.number().min(0).max(1), message: z.string().optional() }) }); // Error stream event export const ErrorStreamEventSchema = BaseStreamEventSchema.extend({ type: z.literal('error'), data: z.object({ streamId: z.string(), code: z.number(), message: z.string(), details: z.any().optional() }) }); // Message stream event export const MessageStreamEventSchema = BaseStreamEventSchema.extend({ type: z.literal('message'), data: SNAPMessageSchema }); // Complete stream event export const CompleteStreamEventSchema = BaseStreamEventSchema.extend({ type: z.literal('complete'), data: z.object({ streamId: z.string(), result: z.any().optional(), summary: z.string().optional() }) }); // Union of all stream events export const StreamEventSchema = z.discriminatedUnion('type', [ StatusStreamEventSchema, ConversationStreamEventSchema, ProgressStreamEventSchema, ErrorStreamEventSchema, MessageStreamEventSchema, CompleteStreamEventSchema ]); // Stream subscription schema - for subscribing to conversation streams export const StreamSubscriptionSchema = z.object({ userId: z.string().regex(/^snap:user:[0-9a-f-]{36}$/), // User subscribing streamTypes: z.array(z.enum([ 'conversations', // Agent-to-agent messages 'status', // What agent is doing 'progress', // Progress updates 'errors' // Problems that occur ])), filter: z.object({ agents: z.array(z.string()).optional(), // Only these agents excludeAgents: z.array(z.string()).optional(), // Not these minCost: z.number().optional(), // Only show interactions above this cost maxCost: z.number().optional() // Only show interactions below this cost }).optional() }); // ========================================== // Extended JSON-RPC Methods // ========================================== export const SNAPMethodSchema = z.enum([ // Core message methods 'message/send', 'message/stream', // Agent discovery 'agent/discover', 'agent/register', 'agent/update', 'agent/remove', // Account linking methods (optional) 'account/link', // Request account linking 'account/status', // Check link status 'account/revoke', // Revoke account link // Task methods 'task/create', 'task/status', 'task/update', 'task/cancel', 'task/list', // Payment methods 'payment/authorize', 'payment/execute', 'payment/status', 'payment/refund', // Stream methods 'stream/start', 'stream/send', 'stream/end', 'stream/subscribe', // Subscribe to conversation streams 'stream/unsubscribe', // Unsubscribe from streams 'stream/filter', // Update stream filters 'stream/pause', // Pause a stream 'stream/resume' // Resume a stream ]); //# sourceMappingURL=types.js.map