/** * YAML Workflow Integration (Lobster-Compatible) * * Parses YAML workflow definitions and converts them to * deterministic state machine definitions. * * Compatible with OpenClaw/Lobster workflow format. */ import { StateMachineDefinition, State, Transition, RetryConfig } from '../core/state-machine'; import { AgentRole } from '../engine/parallel-executor'; // ============================================================================ // Types // ============================================================================ export interface YAMLWorkflow { id: string; name: string; version?: string; description?: string; initial: string; states: Record; events?: string[]; context?: Record; } export interface YAMLState { type: 'start' | 'end' | 'action' | 'parallel' | 'choice' | 'wait' | 'loop' | 'subworkflow'; agent?: string; role?: AgentRole; action?: string; timeout?: number | string; retry?: YAMLRetryConfig; on?: Record; branches?: Record; conditions?: YAMLCondition[]; subworkflow?: string; loop?: YAMLLoopConfig; metadata?: Record; } export interface YAMLTransition { target: string; condition?: YAMLCondition; guard?: string; } export interface YAMLCondition { type: 'equals' | 'contains' | 'exists' | 'custom'; field: string; value?: unknown; } export interface YAMLRetryConfig { maxAttempts: number; backoff?: 'fixed' | 'exponential' | 'linear'; initialDelay?: number | string; maxDelay?: number | string; } export interface YAMLLoopConfig { maxIterations: number; iterator?: string; body: string; exitCondition?: YAMLCondition; } // ============================================================================ // Workflow Parser // ============================================================================ /** * WorkflowParser - Parses YAML workflows to state machine definitions */ export class WorkflowParser { /** * Parse a YAML workflow to a state machine definition */ parse(yaml: YAMLWorkflow): StateMachineDefinition { const states: Record = {}; for (const [stateId, yamlState] of Object.entries(yaml.states)) { states[stateId] = this.parseState(stateId, yamlState); } return { id: yaml.id, name: yaml.name, version: yaml.version || '1.0.0', description: yaml.description, initial: yaml.initial, states, events: yaml.events, context: yaml.context }; } /** * Parse a single state */ private parseState(stateId: string, yamlState: YAMLState): State { const state: State = { id: stateId, name: stateId, type: yamlState.type, agent: yamlState.agent, action: yamlState.action, timeout: this.parseDuration(yamlState.timeout), metadata: { ...yamlState.metadata, role: yamlState.role } }; // Parse retry config if (yamlState.retry) { state.retry = { maxAttempts: yamlState.retry.maxAttempts, backoff: yamlState.retry.backoff || 'exponential', initialDelay: this.parseDuration(yamlState.retry.initialDelay) || 1000, maxDelay: this.parseDuration(yamlState.retry.maxDelay) || 60000 }; } // Parse transitions (on) if (yamlState.on) { const transitions = this.parseTransitions(yamlState.on); state.onExit = transitions; } // Parse parallel branches if (yamlState.branches) { state.type = 'parallel'; state.onEnter = Object.entries(yamlState.branches).map(([event, target]) => ({ event, target })); } // Parse loop config if (yamlState.loop) { state.type = 'loop'; state.metadata = { ...state.metadata, maxIterations: yamlState.loop.maxIterations, iterator: yamlState.loop.iterator, body: yamlState.loop.body }; // Add loop transitions state.onExit = [ { event: 'continue', target: yamlState.loop.body }, { event: 'exit', target: yamlState.on?.['exit'] as string || 'end' } ]; } // Parse subworkflow if (yamlState.subworkflow) { state.type = 'action'; state.action = 'subworkflow'; state.metadata = { ...state.metadata, subworkflow: yamlState.subworkflow }; } return state; } /** * Parse transitions from YAML format */ private parseTransitions(on: Record): Transition[] { const transitions: Transition[] = []; for (const [event, transition] of Object.entries(on)) { if (typeof transition === 'string') { transitions.push({ event, target: transition }); } else { transitions.push({ event, target: transition.target, condition: transition.condition ? this.parseCondition(transition.condition) : undefined, guard: transition.guard }); } } return transitions; } /** * Parse a condition */ private parseCondition(yamlCond: YAMLCondition): Transition['condition'] { return { type: yamlCond.type, field: yamlCond.field, value: yamlCond.value }; } /** * Parse duration string (e.g., '30s', '5m', '1h') */ private parseDuration(duration?: number | string): number | undefined { if (typeof duration === 'number') return duration; if (!duration) return undefined; const match = duration.match(/^(\d+)(ms|s|m|h)?$/); if (!match) return undefined; const value = parseInt(match[1]); const unit = match[2] || 'ms'; switch (unit) { case 'ms': return value; case 's': return value * 1000; case 'm': return value * 60 * 1000; case 'h': return value * 60 * 60 * 1000; default: return value; } } } // ============================================================================ // Workflow Registry // ============================================================================ /** * WorkflowRegistry - Manages workflow definitions */ export class WorkflowRegistry { private workflows: Map = new Map(); private parser: WorkflowParser; constructor() { this.parser = new WorkflowParser(); } /** * Register a workflow from YAML object */ register(yaml: YAMLWorkflow): StateMachineDefinition { this.workflows.set(yaml.id, yaml); return this.parser.parse(yaml); } /** * Get a workflow by ID */ get(id: string): YAMLWorkflow | undefined { return this.workflows.get(id); } /** * Get parsed state machine definition */ getParsed(id: string): StateMachineDefinition | undefined { const yaml = this.workflows.get(id); if (yaml) { return this.parser.parse(yaml); } return undefined; } /** * List all workflows */ list(): string[] { return Array.from(this.workflows.keys()); } } // ============================================================================ // Predefined Workflows // ============================================================================ /** * Standard Code Pipeline Workflow * * Code → Review → Test → Done * With max 3 review iterations */ export const CODE_PIPELINE_WORKFLOW: YAMLWorkflow = { id: 'code-pipeline', name: 'Code Pipeline', version: '1.0.0', description: 'Code → Review → Test pipeline with deterministic flow', initial: 'start', context: { reviewIteration: 0, maxReviewIterations: 3 }, states: { start: { type: 'start', on: { 'start': 'code' } }, code: { type: 'action', role: 'programmer', timeout: '30m', retry: { maxAttempts: 2, backoff: 'exponential', initialDelay: '5s', maxDelay: '1m' }, on: { 'completed': 'review', 'failed': 'failed' } }, review: { type: 'choice', conditions: [ { type: 'equals', field: 'reviewApproved', value: true } ], on: { 'approved': 'test', 'rejected': 'review_loop', 'failed': 'failed' } }, review_loop: { type: 'loop', loop: { maxIterations: 3, body: 'code' }, on: { 'exit': 'failed' } }, test: { type: 'action', role: 'tester', timeout: '15m', on: { 'passed': 'end', 'failed': 'test_failed' } }, test_failed: { type: 'choice', on: { 'retry': 'code', 'abort': 'failed' } }, end: { type: 'end' }, failed: { type: 'end', metadata: { status: 'failed' } } } }; /** * Parallel Multi-Project Workflow * * Runs multiple projects in parallel */ export const PARALLEL_PROJECTS_WORKFLOW: YAMLWorkflow = { id: 'parallel-projects', name: 'Parallel Projects Pipeline', version: '1.0.0', description: 'Run multiple projects in parallel with synchronized completion', initial: 'start', states: { start: { type: 'start', on: { 'start': 'parallel' } }, parallel: { type: 'parallel', branches: { 'project1': 'project1_code', 'project2': 'project2_code', 'project3': 'project3_code', 'project4': 'project4_code' }, on: { 'all_completed': 'end', 'any_failed': 'failed' } }, project1_code: { type: 'action', role: 'programmer', agent: 'project1-programmer', on: { 'completed': 'project1_review' } }, project1_review: { type: 'action', role: 'reviewer', agent: 'project1-reviewer', on: { 'completed': 'project1_test' } }, project1_test: { type: 'action', role: 'tester', agent: 'project1-tester', on: { 'completed': 'join' } }, project2_code: { type: 'action', role: 'programmer', agent: 'project2-programmer', on: { 'completed': 'project2_review' } }, project2_review: { type: 'action', role: 'reviewer', agent: 'project2-reviewer', on: { 'completed': 'project2_test' } }, project2_test: { type: 'action', role: 'tester', agent: 'project2-tester', on: { 'completed': 'join' } }, project3_code: { type: 'action', role: 'programmer', agent: 'project3-programmer', on: { 'completed': 'project3_review' } }, project3_review: { type: 'action', role: 'reviewer', agent: 'project3-reviewer', on: { 'completed': 'project3_test' } }, project3_test: { type: 'action', role: 'tester', agent: 'project3-tester', on: { 'completed': 'join' } }, project4_code: { type: 'action', role: 'programmer', agent: 'project4-programmer', on: { 'completed': 'project4_review' } }, project4_review: { type: 'action', role: 'reviewer', agent: 'project4-reviewer', on: { 'completed': 'project4_test' } }, project4_test: { type: 'action', role: 'tester', agent: 'project4-tester', on: { 'completed': 'join' } }, join: { type: 'wait', on: { 'all_joined': 'end' } }, end: { type: 'end' }, failed: { type: 'end', metadata: { status: 'failed' } } } }; /** * Human-in-the-Loop Workflow */ export const HUMAN_APPROVAL_WORKFLOW: YAMLWorkflow = { id: 'human-approval', name: 'Human Approval Workflow', version: '1.0.0', description: 'Workflow with human approval gates', initial: 'start', states: { start: { type: 'start', on: { 'start': 'plan' } }, plan: { type: 'action', role: 'planner', on: { 'completed': 'await_approval' } }, await_approval: { type: 'wait', timeout: '24h', on: { 'approved': 'execute', 'rejected': 'plan', 'timeout': 'notify_timeout' } }, notify_timeout: { type: 'action', action: 'notify', metadata: { message: 'Approval timeout' }, on: { 'completed': 'await_approval' } }, execute: { type: 'action', role: 'programmer', on: { 'completed': 'review' } }, review: { type: 'action', role: 'reviewer', on: { 'completed': 'end' } }, end: { type: 'end' } } }; // Default registry with predefined workflows export const defaultWorkflowRegistry = new WorkflowRegistry(); // Register predefined workflows defaultWorkflowRegistry.register(CODE_PIPELINE_WORKFLOW); defaultWorkflowRegistry.register(PARALLEL_PROJECTS_WORKFLOW); defaultWorkflowRegistry.register(HUMAN_APPROVAL_WORKFLOW);