// Workflow Builder // Creates clean JSON representations of workflows with step connections import { ConditionalWorkflow, WorkflowStep, WorkflowJSON, StepType, Condition, CreateWorkflowRequest, UpdateWorkflowRequest, workflowToJSON, validateWorkflow } from './conditional-workflow-types'; export class WorkflowBuilder { public workflow: Partial; public steps: Map; public stepOrder: number = 1; constructor(agentId: string, name: string) { this.workflow = { agent_id: agentId, name, status: 'draft', is_default: false, created_at: new Date().toISOString(), updated_at: new Date().toISOString() }; this.steps = new Map(); } // Set workflow metadata setDescription(description: string): this { this.workflow.description = description; return this; } setTriggerPhrase(phrase: string): this { this.workflow.trigger_phrase = phrase; return this; } setAsDefault(isDefault: boolean = true): this { this.workflow.is_default = isDefault; return this; } setStatus(status: 'draft' | 'active' | 'paused' | 'archived'): this { this.workflow.status = status; return this; } setRootStep(stepId: string): this { this.workflow.root_step_id = stepId; return this; } setVariables(variables: Record): this { this.workflow.variables = variables; return this; } // Add steps addInstructionStep( id: string, name: string, instruction: string, options: { description?: string; nextStepId?: string; order?: number; } = {} ): this { const step: WorkflowStep = { id, name, description: options.description, type: 'instruction', order: options.order || this.stepOrder++, instruction, next_step_id: options.nextStepId }; this.steps.set(id, step); return this; } addToolStep( id: string, name: string, toolName: string, options: { description?: string; nextStepId?: string; order?: number; } = {} ): this { const step: WorkflowStep = { id, name, description: options.description, type: 'instruction', order: options.order || this.stepOrder++, tool_name: toolName, next_step_id: options.nextStepId }; this.steps.set(id, step); return this; } addIfStep( id: string, name: string, condition: Condition, options: { description?: string; ifTrueStepId?: string; ifFalseStepId?: string; order?: number; } = {} ): this { const step: WorkflowStep = { id, name, description: options.description, type: 'if', order: options.order || this.stepOrder++, condition, if_true_step_id: options.ifTrueStepId, if_false_step_id: options.ifFalseStepId }; this.steps.set(id, step); return this; } addSequenceStep( id: string, name: string, childStepIds: string[], options: { description?: string; nextStepId?: string; order?: number; } = {} ): this { const step: WorkflowStep = { id, name, description: options.description, type: 'sequence', order: options.order || this.stepOrder++, child_step_ids: childStepIds, next_step_id: options.nextStepId }; this.steps.set(id, step); return this; } addTriggerStep( id: string, name: string, options: { description?: string; nextStepId?: string; order?: number; } = {} ): this { const step: WorkflowStep = { id, name, description: options.description, type: 'trigger', order: options.order || this.stepOrder++, next_step_id: options.nextStepId }; this.steps.set(id, step); return this; } // Update existing steps updateStep( id: string, updates: Partial> ): this { const existingStep = this.steps.get(id); if (existingStep) { this.steps.set(id, { ...existingStep, ...updates }); } return this; } // Connect steps connectSteps(fromStepId: string, toStepId: string): this { const fromStep = this.steps.get(fromStepId); if (fromStep) { fromStep.next_step_id = toStepId; this.steps.set(fromStepId, fromStep); } return this; } connectIfBranch( ifStepId: string, trueStepId?: string, falseStepId?: string ): this { const ifStep = this.steps.get(ifStepId); if (ifStep && ifStep.type === 'if') { if (trueStepId) ifStep.if_true_step_id = trueStepId; if (falseStepId) ifStep.if_false_step_id = falseStepId; this.steps.set(ifStepId, ifStep); } return this; } // Remove steps removeStep(id: string): this { this.steps.delete(id); // Clean up references to this step for (const [stepId, step] of this.steps) { if (step.next_step_id === id) { step.next_step_id = undefined; } if (step.if_true_step_id === id) { step.if_true_step_id = undefined; } if (step.if_false_step_id === id) { step.if_false_step_id = undefined; } if (step.child_step_ids?.includes(id)) { step.child_step_ids = step.child_step_ids.filter(childId => childId !== id); } } // Update root step if needed if (this.workflow.root_step_id === id) { this.workflow.root_step_id = undefined; } return this; } // Build workflow build(): ConditionalWorkflow { if (!this.workflow.id) { this.workflow.id = `workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } const workflow = { ...this.workflow, steps: Array.from(this.steps.values()).sort((a, b) => a.order - b.order) } as ConditionalWorkflow; // Auto-set root step if not set if (!workflow.root_step_id && workflow.steps.length > 0) { workflow.root_step_id = workflow.steps[0].id; } return workflow; } // Build JSON buildJSON(): WorkflowJSON { const workflow = this.build(); return workflowToJSON(workflow); } // Build API request buildCreateRequest(): CreateWorkflowRequest { const workflow = this.build(); return { name: workflow.name, description: workflow.description, trigger_phrase: workflow.trigger_phrase, is_default: workflow.is_default, steps: workflow.steps.map(step => ({ name: step.name, description: step.description, type: step.type, instruction: step.instruction, tool_name: step.tool_name, condition: step.condition, if_true_step_id: step.if_true_step_id, if_false_step_id: step.if_false_step_id, child_step_ids: step.child_step_ids, next_step_id: step.next_step_id, order: step.order })), root_step_id: workflow.root_step_id, variables: workflow.variables }; } // Validation validate(): { isValid: boolean; errors: string[] } { const workflow = this.build(); return validateWorkflow(workflow); } // Clone builder clone(): WorkflowBuilder { const newBuilder = new WorkflowBuilder( this.workflow.agent_id!, this.workflow.name! ); newBuilder.workflow = { ...this.workflow }; newBuilder.steps = new Map(this.steps); newBuilder.stepOrder = this.stepOrder; return newBuilder; } } // Helper functions for common workflow patterns // Linear workflow: Step 1 → Step 2 → Step 3 export function createLinearWorkflow( agentId: string, name: string, steps: Array<{ id: string; name: string; instruction: string; description?: string; }> ): ConditionalWorkflow { const builder = new WorkflowBuilder(agentId, name); for (let i = 0; i < steps.length; i++) { const step = steps[i]; const nextStepId = i < steps.length - 1 ? steps[i + 1].id : undefined; builder.addInstructionStep(step.id, step.name, step.instruction, { description: step.description, nextStepId }); } return builder.build(); } // Conditional workflow: Step 1 → Condition → Branch A or Branch B export function createConditionalWorkflow( agentId: string, name: string, initialStep: { id: string; name: string; instruction: string }, condition: { id: string; name: string; condition: Condition }, trueBranch: { id: string; name: string; instruction: string }, falseBranch: { id: string; name: string; instruction: string } ): ConditionalWorkflow { const builder = new WorkflowBuilder(agentId, name); // Add initial step builder.addInstructionStep( initialStep.id, initialStep.name, initialStep.instruction, { nextStepId: condition.id } ); // Add condition step builder.addIfStep( condition.id, condition.name, condition.condition, { ifTrueStepId: trueBranch.id, ifFalseStepId: falseBranch.id } ); // Add branches builder.addInstructionStep( trueBranch.id, trueBranch.name, trueBranch.instruction ); builder.addInstructionStep( falseBranch.id, falseBranch.name, falseBranch.instruction ); return builder.build(); } // Create workflow from existing data export function createWorkflowFromJSON(json: WorkflowJSON): WorkflowBuilder { const builder = new WorkflowBuilder( json.workflow.agent_id, json.workflow.name ); builder.workflow.id = json.workflow.id; builder.workflow.description = json.workflow.description; builder.workflow.status = json.workflow.status; builder.workflow.trigger_phrase = json.workflow.trigger_phrase; builder.workflow.is_default = json.workflow.is_default; builder.workflow.created_at = json.workflow.created_at; builder.workflow.updated_at = json.workflow.updated_at; builder.workflow.root_step_id = json.flow.root_step_id; builder.workflow.variables = json.flow.variables; // Add all steps for (const step of json.flow.steps) { builder.steps.set(step.id, step); } return builder; } // Utility to generate step IDs export function generateStepId(prefix: string = 'step'): string { return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; } // Utility to generate workflow ID export function generateWorkflowId(): string { return `workflow_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // Export all step types for easy reference export const STEP_TYPES: Record = { INSTRUCTION: 'instruction', IF: 'if', SEQUENCE: 'sequence', TRIGGER: 'trigger' }; // Export condition operations export const CONDITION_OPERATIONS = { CONTAINS: 'contains', EQUALS: 'equals', NOT_EQUALS: 'not_equals', IS_EMPTY: 'is_empty', IS_NOT_EMPTY: 'is_not_empty' } as const;