Markdown Converter
Agent skill for markdown-converter
**Project**: JetVision AI Assistant
Sign in to like and favorite skills
Project: JetVision AI Assistant Architecture: Multi-Agent System with OpenAI Agent SDK + MCP Stack: Next.js 14, TypeScript, Supabase, BullMQ + Redis
# Start app + MCP servers concurrently npm run dev # Start Next.js app only npm run dev:app # Start MCP servers only npm run dev:mcp # Build for production npm run build # Start production server npm start # Lint code npm run lint
# Run all tests npm test # Run unit tests only npm run test:unit # Run integration tests only npm run test:integration # Run agent tests only npm run test:agents # Watch mode (development) npm run test:watch # Coverage report (75% threshold) npm run test:coverage
# Create new agent scaffold npm run agents:create # List all registered agents npm run agents:list
# Create new MCP server npm run mcp:create # Test MCP connection npm run mcp:test # List available MCP tools npm run mcp:list-tools
The system consists of 6 specialized AI agents coordinating through an internal Agent-to-Agent (A2A) communication layer:
┌─────────────────────────────────────────────────┐ │ Agent Core System │ │ • BaseAgent - Abstract foundation │ │ • AgentFactory - Creates agents (Singleton) │ │ • AgentRegistry - Central registry (Singleton) │ │ • AgentContext - Session management │ └─────────────────┬───────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ Agent Coordination Layer │ │ • MessageBus - Event-driven A2A (EventEmitter) │ │ • HandoffManager - Task delegation │ │ • TaskQueue - Async processing (BullMQ+Redis) │ │ • WorkflowStateMachine - State management │ └─────────────────┬───────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────┐ │ 6 Specialized Agents │ │ • OrchestratorAgent │ │ • ClientDataAgent │ │ • FlightSearchAgent │ │ • ProposalAnalysisAgent │ │ • CommunicationAgent │ │ • ErrorMonitorAgent │ └─────────────────────────────────────────────────┘
// agents/core/types.ts enum AgentType { ORCHESTRATOR = 'orchestrator', // Analyzes RFP, delegates tasks CLIENT_DATA = 'client_data', // Fetches client profile FLIGHT_SEARCH = 'flight_search', // Searches flights via Avinode PROPOSAL_ANALYSIS = 'proposal_analysis', // Scores and ranks quotes COMMUNICATION = 'communication', // Generates and sends emails ERROR_MONITOR = 'error_monitor', // Monitors errors, retries }
/agents/core)import { AgentFactory, AgentType } from '@agents/core' // Get singleton factory const factory = AgentFactory.getInstance() // Create and initialize agent const agent = await factory.createAndInitialize({ type: AgentType.ORCHESTRATOR, name: 'RFP Orchestrator', model: 'gpt-4-turbo-preview', temperature: 0.7, }) // Execute agent const result = await agent.execute({ sessionId: 'session-123', requestId: 'request-456', userId: 'user-abc', })
All agents extend
BaseAgent from agents/core/base-agent.ts:
abstract class BaseAgent implements IAgent { // Required implementation abstract execute(context: AgentContext): Promise<AgentResult> // Available methods registerTool(tool: AgentTool): void async handoff(toAgent: string, task: AgentTask): Promise<void> protected async createChatCompletion(messages: AgentMessage[]): Promise<ChatCompletion> getMetrics(): AgentMetrics async shutdown(): Promise<void> }
/agents/coordination)import { messageBus, MessageType } from '@agents/coordination' // Subscribe to messages const unsubscribe = messageBus.subscribe( MessageType.TASK_COMPLETED, async (message) => { console.log(`Task ${message.payload.taskId} completed by ${message.sourceAgent}`) } ) // Publish a message await messageBus.publish({ type: MessageType.TASK_STARTED, sourceAgent: 'agent-123', targetAgent: 'agent-456', payload: { taskId: 'task-789' }, context: { sessionId: 'session-abc' }, }) // Cleanup unsubscribe()
7 Message Types:
TASK_CREATED - New task createdTASK_STARTED - Agent started workingTASK_COMPLETED - Task finished successfullyTASK_FAILED - Task execution failedAGENT_HANDOFF - Task delegated to another agentCONTEXT_UPDATE - Shared context updatedERROR - Error occurredimport { handoffManager } from '@agents/coordination' // Agent A hands off to Agent B await handoffManager.handoff({ fromAgent: 'orchestrator-id', toAgent: 'client-data-id', task: { id: 'task-123', type: 'fetch_client_data', payload: { requestId: 'req-456' }, priority: 'high', status: 'pending', }, context: { sessionId: 'session-789' }, reason: 'Need client profile before flight search', }) // Agent B accepts handoff const task = await handoffManager.acceptHandoff('task-123', 'client-data-id') // Or rejects it await handoffManager.rejectHandoff('task-123', 'client-data-id', 'Agent busy')
import { AgentTaskQueue } from '@agents/coordination' const queue = new AgentTaskQueue() // Add task with priority await queue.addTask( { id: 'task-123', type: 'search_flights', payload: { /* data */ }, priority: 'urgent', // urgent | high | normal | low }, context, { priority: 1, attempts: 3 } ) // Start worker to process tasks await queue.startWorker(async (job) => { const { task, context } = job.data // Process task return { success: true, data: result } })
Priority Levels:
urgent → Priority 1 (immediate)high → Priority 2normal → Priority 5 (default)low → Priority 10import { workflowManager, WorkflowState } from '@agents/coordination' // Create workflow const workflow = workflowManager.createWorkflow('request-123') // Transition through states workflow.transition(WorkflowState.ANALYZING, 'orchestrator-agent') workflow.transition(WorkflowState.FETCHING_CLIENT_DATA, 'client-data-agent') workflow.transition(WorkflowState.SEARCHING_FLIGHTS, 'flight-search-agent') // Check status console.log(workflow.getState()) // SEARCHING_FLIGHTS console.log(workflow.isInProgress()) // true console.log(workflow.getDuration()) // Duration in ms // Get per-state timings const timings = workflow.getStateTimings() console.log(`Time analyzing: ${timings[WorkflowState.ANALYZING]}ms`)
11 Workflow States:
CREATED ↓ ANALYZING ↓ FETCHING_CLIENT_DATA ↓ SEARCHING_FLIGHTS ↓ AWAITING_QUOTES ↓ ANALYZING_PROPOSALS ↓ GENERATING_EMAIL ↓ SENDING_PROPOSAL ↓ COMPLETED / FAILED / CANCELLED (terminal states)
Configured in
vitest.config.ts and tsconfig.json:
import { BaseAgent } from '@agents/core' // agents/core import { messageBus } from '@agents/coordination' // agents/coordination import { supabase } from '@/lib/supabase' // lib/supabase import { Button } from '@components/ui/button' // components/ui/button
Available Aliases:
@/ → Root directory@agents/ → agents/@lib/ → lib/@mcp-servers/ → mcp-servers/@components/ → components/@tests/ → __tests__/v0-jetvision-assistant/ ├── agents/ │ ├── core/ # ✅ Complete - Agent foundation │ │ ├── base-agent.ts # Abstract base class │ │ ├── agent-factory.ts # Singleton factory │ │ ├── agent-registry.ts # Central registry │ │ ├── agent-context.ts # Context manager │ │ ├── types.ts # Type definitions │ │ └── index.ts # Barrel exports │ │ │ ├── coordination/ # ✅ Complete - A2A coordination │ │ ├── message-bus.ts # EventEmitter-based messaging │ │ ├── handoff-manager.ts # Task delegation │ │ ├── task-queue.ts # BullMQ async queue │ │ ├── state-machine.ts # Workflow states │ │ └── index.ts # Barrel exports │ │ │ ├── implementations/ # 🚧 Pending - Specific agents │ ├── tools/ # 🚧 Pending - Agent tools │ ├── guardrails/ # 🚧 Pending - Safety checks │ └── monitoring/ # 🚧 Pending - Observability │ ├── mcp-servers/ # 🚧 Pending - MCP servers │ └── shared/ # Shared MCP utilities │ ├── lib/ │ ├── supabase/ # ✅ Supabase client │ ├── config/ # ✅ Configuration │ ├── types/ # ✅ Shared types │ └── utils/ # ✅ Utilities │ ├── __tests__/ # ✅ Structure ready │ ├── unit/ # Unit tests │ ├── integration/ # Integration tests │ ├── e2e/ # End-to-end tests │ └── mocks/ # Test mocks │ ├── docs/ │ ├── architecture/ # Architecture docs │ │ ├── MULTI_AGENT_SYSTEM.md # Complete guide │ │ └── IMPLEMENTATION_SUMMARY.md # Phase 1 summary │ ├── AGENTS.md # Agent creation guidelines │ ├── GETTING_STARTED.md # Getting started guide │ └── SYSTEM_ARCHITECTURE.md # System overview │ ├── package.json # Dependencies + scripts ├── vitest.config.ts # Testing configuration ├── next.config.mjs # Next.js config ├── tsconfig.json # TypeScript config └── .env.local # Environment variables (create this)
Create
.env.local in the project root:
# OpenAI OPENAI_API_KEY=sk-... OPENAI_ORGANIZATION_ID=org-... # Redis (for task queue) REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= # Supabase NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key SUPABASE_SERVICE_ROLE_KEY=your-service-role-key # Clerk (Authentication) NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_... CLERK_SECRET_KEY=sk_...
Start Redis (required for task queue):
# Docker (recommended) docker run -d -p 6379:6379 redis:latest # Or local install brew install redis && brew services start redis # macOS
From
docs/AGENTS.md:
Configured in
vitest.config.ts:
// __tests__/unit/agents/base-agent.test.ts import { describe, it, expect, beforeEach, vi } from 'vitest' import { BaseAgent } from '@agents/core' describe('BaseAgent', () => { let agent: TestAgent beforeEach(() => { agent = new TestAgent({ type: AgentType.ORCHESTRATOR, name: 'Test' }) }) it('should initialize correctly', async () => { await agent.initialize() expect(agent.status).toBe(AgentStatus.IDLE) }) it('should track metrics', async () => { await agent.execute(context) const metrics = agent.getMetrics() expect(metrics.totalExecutions).toBe(1) }) })
From
docs/AGENTS.md:
OrchestratorAgent)fetchClientData)MAX_RETRIES)I prefix (e.g., IAgent)AgentConfig)AgentType)any: Use proper types or unknownUsed for: AgentFactory, AgentRegistry, MessageBus, HandoffManager, WorkflowStateManager
class MyService { private static instance: MyService static getInstance(): MyService { if (!MyService.instance) { MyService.instance = new MyService() } return MyService.instance } private constructor() { // Prevent direct instantiation } }
Used for: Creating agent instances
// Register agent type factory.registerAgentType(AgentType.ORCHESTRATOR, OrchestratorAgent) // Create agent const agent = factory.createAgent({ type: AgentType.ORCHESTRATOR, name: 'Orchestrator' })
Used for: Message bus, event-driven communication
// Subscribe const unsubscribe = messageBus.subscribe(MessageType.TASK_COMPLETED, handler) // Publish await messageBus.publish({ type: MessageType.TASK_COMPLETED, ... }) // Cleanup unsubscribe()
Used for: Workflow management with enforced transitions
const workflow = new WorkflowStateMachine('request-123') workflow.transition(WorkflowState.ANALYZING, 'agent-id') // Will throw if invalid transition
MULTI_AGENT_QUICKSTART.md - 5-minute setup guidedocs/architecture/MULTI_AGENT_SYSTEM.md - Complete system architecture (400+ lines)docs/architecture/IMPLEMENTATION_SUMMARY.md - Phase 1 completion summarydocs/AGENTS.md - Agent creation guidelines, code style, testingdocs/GETTING_STARTED.md - Original getting started guidedocs/SYSTEM_ARCHITECTURE.md - System overviewExample end-to-end workflow:
1. User submits RFP ↓ 2. Orchestrator Agent • Analyzes request • Creates workflow state machine • Publishes TASK_CREATED ↓ 3. Client Data Manager Agent • Accepts handoff • Fetches client profile (Google Sheets MCP) • Updates context • Hands off to Flight Search ↓ 4. Flight Search Agent • Searches flights (Avinode MCP) • Creates RFP in Avinode • Updates state: AWAITING_QUOTES ↓ 5. Proposal Analysis Agent • Triggered by webhook • Scores all quotes • Ranks proposals • Hands off to Communication ↓ 6. Communication Manager Agent • Generates email (OpenAI) • Creates PDF • Sends email (Gmail MCP) • Updates state: COMPLETED
.env.local or .env files# Start Redis docker run -d -p 6379:6379 redis:latest
Add to
.env.local:
OPENAI_API_KEY=sk-your-key-here
npm install # Reinstall dependencies
npm run test:coverage # Check coverage report npm run test:watch # Debug in watch mode
Built with: Next.js 14, OpenAI Agent SDK, TypeScript, Supabase, BullMQ, Vitest
For detailed architecture, see
docs/architecture/MULTI_AGENT_SYSTEM.md