@@ -3,7 +3,7 @@ import { v4 as uuidv4 } from 'uuid'
33import { createLogger } from '@/lib/logs/console/logger'
44import { buildTraceSpans } from '@/lib/logs/execution/trace-spans/trace-spans'
55import { processStreamingBlockLogs } from '@/lib/tokenization'
6- import { TriggerUtils } from '@/lib/workflows/triggers'
6+ import { resolveStartCandidates , StartBlockPath , TriggerUtils } from '@/lib/workflows/triggers'
77import type { BlockOutput } from '@/blocks/types'
88import { Executor } from '@/executor'
99import type { BlockLog , ExecutionResult , StreamingExecution } from '@/executor/types'
@@ -824,132 +824,76 @@ export function useWorkflowExecution() {
824824
825825 startBlockId = startBlock . blockId
826826 } else {
827- // For manual editor runs: look for Manual trigger OR API trigger
828- const entries = Object . entries ( filteredStates )
829-
830- // Find manual triggers and API triggers
831- const manualTriggers = TriggerUtils . findTriggersByType ( filteredStates , 'manual' )
832- const apiTriggers = TriggerUtils . findTriggersByType ( filteredStates , 'api' )
833-
834- logger . info ( 'Manual run trigger check:' , {
835- manualTriggersCount : manualTriggers . length ,
836- apiTriggersCount : apiTriggers . length ,
837- manualTriggers : manualTriggers . map ( ( t ) => ( {
838- type : t . type ,
839- name : t . name ,
840- isLegacy : t . type === 'starter' ,
841- } ) ) ,
842- apiTriggers : apiTriggers . map ( ( t ) => ( {
843- type : t . type ,
844- name : t . name ,
845- isLegacy : t . type === 'starter' ,
827+ const candidates = resolveStartCandidates ( filteredStates , {
828+ execution : 'manual' ,
829+ } )
830+
831+ logger . info ( 'Manual run start candidates:' , {
832+ count : candidates . length ,
833+ paths : candidates . map ( ( candidate ) => ( {
834+ path : candidate . path ,
835+ type : candidate . block . type ,
836+ name : candidate . block . name ,
846837 } ) ) ,
847838 } )
848839
849- let selectedTrigger : any = null
850- let selectedBlockId : string | null = null
851-
852- // Check for API triggers first (they take precedence over manual triggers)
853- if ( apiTriggers . length === 1 ) {
854- selectedTrigger = apiTriggers [ 0 ]
855- const blockEntry = entries . find ( ( [ , block ] ) => block === selectedTrigger )
856- if ( blockEntry ) {
857- selectedBlockId = blockEntry [ 0 ]
858-
859- // Extract test values from the API trigger's inputFormat
860- if (
861- selectedTrigger . type === 'api_trigger' ||
862- selectedTrigger . type === 'start_trigger' ||
863- selectedTrigger . type === 'starter'
864- ) {
865- const inputFormatValue = selectedTrigger . subBlocks ?. inputFormat ?. value
866- const testInput = extractTestValuesFromInputFormat ( inputFormatValue )
867-
868- // Use the test input as workflow input
869- if ( Object . keys ( testInput ) . length > 0 ) {
870- finalWorkflowInput = testInput
871- logger . info ( 'Using trigger test values for manual run:' , testInput )
872- }
873- }
874- }
875- } else if ( apiTriggers . length > 1 ) {
840+ const apiCandidates = candidates . filter (
841+ ( candidate ) => candidate . path === StartBlockPath . SPLIT_API
842+ )
843+ if ( apiCandidates . length > 1 ) {
876844 const error = new Error ( 'Multiple API Trigger blocks found. Keep only one.' )
877845 logger . error ( 'Multiple API triggers found' )
878846 setIsExecuting ( false )
879847 throw error
880- } else if ( manualTriggers . length >= 1 ) {
881- // No API trigger, check for manual triggers
882- // Prefer manual_trigger over input_trigger for simple runs
883- const manualTrigger = manualTriggers . find ( ( t ) => t . type === 'manual_trigger' )
884- const inputTrigger = manualTriggers . find ( ( t ) => t . type === 'input_trigger' )
885-
886- selectedTrigger = manualTrigger || inputTrigger || manualTriggers [ 0 ]
887- const blockEntry = entries . find ( ( [ , block ] ) => block === selectedTrigger )
888- if ( blockEntry ) {
889- selectedBlockId = blockEntry [ 0 ]
890-
891- // Extract test values from input trigger's inputFormat if it's an input_trigger or start_trigger
892- if (
893- selectedTrigger . type === 'input_trigger' ||
894- selectedTrigger . type === 'start_trigger'
895- ) {
896- const inputFormatValue = selectedTrigger . subBlocks ?. inputFormat ?. value
897- const testInput = extractTestValuesFromInputFormat ( inputFormatValue )
898-
899- // Use the test input as workflow input
900- if ( Object . keys ( testInput ) . length > 0 ) {
901- finalWorkflowInput = testInput
902- logger . info ( 'Using trigger test values for manual run:' , testInput )
903- }
904- }
905- }
906- } else {
907- // Fallback: Check for legacy starter block
908- const starterBlock = Object . values ( filteredStates ) . find ( ( block ) => block . type === 'starter' )
909- if ( starterBlock ) {
910- // Found a legacy starter block, use it as a manual trigger
911- const blockEntry = Object . entries ( filteredStates ) . find (
912- ( [ , block ] ) => block === starterBlock
913- )
914- if ( blockEntry ) {
915- selectedBlockId = blockEntry [ 0 ]
916- selectedTrigger = starterBlock
917- logger . info ( 'Using legacy starter block for manual run' )
918- }
919- }
848+ }
920849
921- if ( ! selectedBlockId || ! selectedTrigger ) {
922- const error = new Error ( 'Manual run requires a Manual, Input Form, or API Trigger block' )
923- logger . error ( 'No manual/input or API triggers found for manual run' )
850+ const selectedCandidate = apiCandidates [ 0 ] ?? candidates [ 0 ]
851+
852+ if ( ! selectedCandidate ) {
853+ const error = new Error ( 'Manual run requires a Manual, Input Form, or API Trigger block' )
854+ logger . error ( 'No manual/input or API triggers found for manual run' )
855+ setIsExecuting ( false )
856+ throw error
857+ }
858+
859+ startBlockId = selectedCandidate . blockId
860+ const selectedTrigger = selectedCandidate . block
861+
862+ if ( selectedCandidate . path !== StartBlockPath . LEGACY_STARTER ) {
863+ const outgoingConnections = workflowEdges . filter ( ( edge ) => edge . source === startBlockId )
864+ if ( outgoingConnections . length === 0 ) {
865+ const triggerName = selectedTrigger . name || selectedTrigger . type
866+ const error = new Error ( `${ triggerName } must be connected to other blocks to execute` )
867+ logger . error ( 'Trigger has no outgoing connections' , { triggerName, startBlockId } )
924868 setIsExecuting ( false )
925869 throw error
926870 }
927871 }
928872
929- if ( selectedBlockId && selectedTrigger ) {
930- startBlockId = selectedBlockId
931-
932- // Check if the trigger has any outgoing connections (except for legacy starter blocks)
933- // Legacy starter blocks have their own validation in the executor
934- if ( selectedTrigger . type !== 'starter' ) {
935- const outgoingConnections = workflowEdges . filter ( ( edge ) => edge . source === startBlockId )
936- if ( outgoingConnections . length === 0 ) {
937- const triggerName = selectedTrigger . name || selectedTrigger . type
938- const error = new Error ( `${ triggerName } must be connected to other blocks to execute` )
939- logger . error ( 'Trigger has no outgoing connections' , { triggerName, startBlockId } )
940- setIsExecuting ( false )
941- throw error
942- }
873+ if (
874+ selectedCandidate . path === StartBlockPath . SPLIT_API ||
875+ selectedCandidate . path === StartBlockPath . SPLIT_INPUT ||
876+ selectedCandidate . path === StartBlockPath . UNIFIED
877+ ) {
878+ const inputFormatValue = selectedTrigger . subBlocks ?. inputFormat ?. value
879+ const testInput = extractTestValuesFromInputFormat ( inputFormatValue )
880+
881+ if ( Object . keys ( testInput ) . length > 0 ) {
882+ finalWorkflowInput = testInput
883+ logger . info ( 'Using trigger test values for manual run:' , {
884+ startBlockId,
885+ testFields : Object . keys ( testInput ) ,
886+ path : selectedCandidate . path ,
887+ } )
943888 }
944-
945- logger . info ( 'Trigger found for manual run:' , {
946- startBlockId,
947- triggerType : selectedTrigger . type ,
948- triggerName : selectedTrigger . name ,
949- isLegacyStarter : selectedTrigger . type === 'starter' ,
950- usingTestValues : selectedTrigger . type === 'api_trigger' ,
951- } )
952889 }
890+
891+ logger . info ( 'Trigger found for manual run:' , {
892+ startBlockId,
893+ triggerType : selectedTrigger . type ,
894+ triggerName : selectedTrigger . name ,
895+ startPath : selectedCandidate . path ,
896+ } )
953897 }
954898
955899 // If we don't have a valid startBlockId at this point, throw an error
0 commit comments