(
workflowState: WorkflowState,
options: { sanitize?: boolean } = {}
)
| 160 | * Checks all tool references, block types, and required fields |
| 161 | */ |
| 162 | export function validateWorkflowState( |
| 163 | workflowState: WorkflowState, |
| 164 | options: { sanitize?: boolean } = {} |
| 165 | ): WorkflowValidationResult { |
| 166 | const errors: string[] = [] |
| 167 | const warnings: string[] = [] |
| 168 | let sanitizedState = workflowState |
| 169 | |
| 170 | try { |
| 171 | // Basic structure validation |
| 172 | if (!workflowState || typeof workflowState !== 'object') { |
| 173 | errors.push('Invalid workflow state: must be an object') |
| 174 | return { valid: false, errors, warnings } |
| 175 | } |
| 176 | |
| 177 | if (!workflowState.blocks || typeof workflowState.blocks !== 'object') { |
| 178 | errors.push('Invalid workflow state: missing blocks') |
| 179 | return { valid: false, errors, warnings } |
| 180 | } |
| 181 | |
| 182 | // Validate each block |
| 183 | const sanitizedBlocks: Record<string, BlockState> = {} |
| 184 | let hasChanges = false |
| 185 | |
| 186 | for (const [blockId, block] of Object.entries(workflowState.blocks)) { |
| 187 | if (!block || typeof block !== 'object') { |
| 188 | errors.push(`Block ${blockId}: invalid block structure`) |
| 189 | continue |
| 190 | } |
| 191 | |
| 192 | // Check if block type exists |
| 193 | const blockConfig = getBlock(block.type) |
| 194 | |
| 195 | // Special handling for container blocks (loop and parallel) |
| 196 | if (block.type === 'loop' || block.type === 'parallel') { |
| 197 | // These are valid container types, they don't need block configs |
| 198 | sanitizedBlocks[blockId] = block |
| 199 | continue |
| 200 | } |
| 201 | |
| 202 | if (!blockConfig) { |
| 203 | errors.push(`Block ${block.name || blockId}: unknown block type '${block.type}'`) |
| 204 | if (options.sanitize) { |
| 205 | hasChanges = true |
| 206 | continue // Skip this block in sanitized output |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | // Validate tool references in blocks that use tools |
| 211 | if (block.type === 'api' || block.type === 'generic') { |
| 212 | // For API and generic blocks, the tool is determined by the block's tool configuration |
| 213 | // In the workflow state, we need to check if the block type has valid tool access |
| 214 | const blockConfig = getBlock(block.type) |
| 215 | if (blockConfig?.tools?.access) { |
| 216 | // API block has static tool access |
| 217 | const toolIds = blockConfig.tools.access |
| 218 | for (const toolId of toolIds) { |
| 219 | const validationError = validateToolReference(toolId, block.type, block.name) |
no test coverage detected