3232from src .utils import debug_log , log , error_exit
3333from src import telemetry_handler
3434from src .version_check import do_version_check
35+ from src .smartfix .domains .workflow .session_handler import create_session_handler , QASectionConfig
3536from src .smartfix .shared .failure_categories import FailureCategory
3637
3738# Import domain-specific handlers
@@ -352,15 +353,19 @@ def main(): # noqa: C901
352353 vuln_title = vulnerability_data ['vulnerabilityTitle' ]
353354 remediation_id = vulnerability_data ['remediationId' ]
354355 session_id = vulnerability_data .get ('sessionId' )
355- previous_vuln_uuid = vuln_uuid # Update tracking variable
356-
357- # Create prompt configuration for SmartFix agent
358- prompts = PromptConfiguration .for_smartfix_agent (
359- fix_system_prompt = vulnerability_data ['fixSystemPrompt' ],
360- fix_user_prompt = vulnerability_data ['fixUserPrompt' ],
361- qa_system_prompt = vulnerability_data ['qaSystemPrompt' ],
362- qa_user_prompt = vulnerability_data ['qaUserPrompt' ]
363- )
356+
357+ # Validate and create prompt configuration for SmartFix agent
358+ try :
359+ PromptConfiguration .validate_raw_prompts_data (vulnerability_data )
360+ prompts = PromptConfiguration .for_smartfix_agent (
361+ fix_system_prompt = vulnerability_data ['fixSystemPrompt' ],
362+ fix_user_prompt = vulnerability_data ['fixUserPrompt' ],
363+ qa_system_prompt = vulnerability_data ['qaSystemPrompt' ],
364+ qa_user_prompt = vulnerability_data ['qaUserPrompt' ]
365+ )
366+ except ValueError as e :
367+ log (f"Error: Invalid prompts from backend: { e } " , is_error = True )
368+ error_exit (remediation_id , FailureCategory .GENERAL_FAILURE .value )
364369 else :
365370 # For external coding agents (GITHUB_COPILOT/CLAUDE_CODE), get vulnerability details
366371 log ("\n ::group::--- Fetching next vulnerability details from Contrast API ---" )
@@ -381,12 +386,11 @@ def main(): # noqa: C901
381386 # Check if this is the same vulnerability UUID as the previous iteration
382387 if vuln_uuid == previous_vuln_uuid :
383388 log (f"Error: Backend provided the same vulnerability UUID ({ vuln_uuid } ) as the previous iteration. This indicates a backend error." , is_warning = True )
384- error_exit (remediation_id , "DUPLICATE_VULNERABILITY_UUID" )
389+ error_exit (remediation_id , FailureCategory . GENERAL_FAILURE . value )
385390
386391 vuln_title = vulnerability_data ['vulnerabilityTitle' ]
387392 remediation_id = vulnerability_data ['remediationId' ]
388393 session_id = None # External agents don't use Contrast LLM sessions
389- previous_vuln_uuid = vuln_uuid # Update tracking variable
390394
391395 # Create prompt configuration for external agent (no prompts required)
392396 prompts = PromptConfiguration .for_external_agent ()
@@ -414,6 +418,10 @@ def main(): # noqa: C901
414418 else :
415419 log (f"No existing OPEN or MERGED PR found for vulnerability { vuln_uuid } . Proceeding with fix attempt." )
416420 log ("\n ::endgroup::" )
421+
422+ # Update tracking variable now that we know we're actually processing this vuln
423+ previous_vuln_uuid = vuln_uuid
424+
417425 log (f"\n \033 [0;33m Selected vuln to fix: { vuln_title } \033 [0m" )
418426
419427 # --- Create Common Remediation Context ---
@@ -460,13 +468,33 @@ def main(): # noqa: C901
460468 session = smartfix_agent .remediate (context )
461469
462470 # Extract results from the session
463- if session .success :
464- ai_fix_summary_full = session .pr_body if session .pr_body else "Fix completed successfully"
465- else :
466- # Agent failed - handle the error
467- failure_category = session .failure_category .value if session .failure_category else FailureCategory .AGENT_FAILURE .value
468- log (f"Agent failed with reason: { failure_category } " )
469- error_exit (remediation_id , failure_category )
471+ session_handler = create_session_handler ()
472+ session_result = session_handler .handle_session_result (session )
473+
474+ if not session_result .should_continue :
475+ # QA Agent failed to fix the build
476+ log (f"Agent failed with reason: { session_result .failure_category } " )
477+ git_handler .cleanup_branch (new_branch_name )
478+ contrast_api .notify_remediation_failed (
479+ remediation_id = remediation_id ,
480+ failure_category = session_result .failure_category ,
481+ contrast_host = config .CONTRAST_HOST ,
482+ contrast_org_id = config .CONTRAST_ORG_ID ,
483+ contrast_app_id = config .CONTRAST_APP_ID ,
484+ contrast_auth_key = config .CONTRAST_AUTHORIZATION_KEY ,
485+ contrast_api_key = config .CONTRAST_API_KEY
486+ )
487+ continue # Move to next vulnerability
488+
489+ ai_fix_summary_full = session_result .ai_fix_summary
490+ # Generate QA section based on session results
491+ # The SmartFix agent already handled the QA loop internally
492+ qa_config = QASectionConfig (
493+ skip_qa_review = config .SKIP_QA_REVIEW ,
494+ has_build_command = build_config .has_build_command (),
495+ build_command = build_config .build_command
496+ )
497+ qa_section = session_handler .generate_qa_section (session , qa_config )
470498
471499 # --- Git and GitHub Operations ---
472500 # All file changes from the agent (fix + QA + formatting) are uncommitted at this point
@@ -486,54 +514,6 @@ def main(): # noqa: C901
486514 git_handler .commit_changes (commit_message )
487515 log ("Committed all agent changes." )
488516
489- # Generate QA section based on session results
490- # The SmartFix agent already handled the QA loop internally
491- qa_section = "\n \n ---\n \n ## Review \n \n "
492-
493- if not config .SKIP_QA_REVIEW and build_config .has_build_command ():
494- # QA was expected to run - check session results
495- if session .qa_attempts > 0 : # QA was attempted
496- qa_section += f"* **Build Run:** Yes (`{ build_config .build_command } `)\n "
497- if session .success :
498- qa_section += "* **Final Build Status:** Success \n "
499- else :
500- qa_section += "* **Final Build Status:** Failure \n "
501-
502- # Check if we should skip PR creation due to QA failure
503- log ("\n --- Skipping PR creation as QA Agent failed to fix build issues ---" )
504-
505- # Get failure category directly from session
506- failure_category = session .failure_category .value if session .failure_category else FailureCategory .QA_AGENT_FAILURE .value
507-
508- # Notify the Remediation service about the failed remediation
509- remediation_notified = contrast_api .notify_remediation_failed (
510- remediation_id = remediation_id ,
511- failure_category = failure_category ,
512- contrast_host = config .CONTRAST_HOST ,
513- contrast_org_id = config .CONTRAST_ORG_ID ,
514- contrast_app_id = config .CONTRAST_APP_ID ,
515- contrast_auth_key = config .CONTRAST_AUTHORIZATION_KEY ,
516- contrast_api_key = config .CONTRAST_API_KEY
517- )
518-
519- if remediation_notified :
520- log (f"Successfully notified Remediation service about { failure_category } for remediation { remediation_id } ." )
521- else :
522- log (f"Failed to notify Remediation service about { failure_category } for remediation { remediation_id } ." , is_warning = True )
523-
524- git_handler .cleanup_branch (new_branch_name )
525- contrast_api .send_telemetry_data ()
526- continue # Move to the next vulnerability
527- else :
528- qa_section += "* **Build Run:** No (BUILD_COMMAND not provided)\n "
529- qa_section += "* **Final Build Status:** Skipped\n "
530- else : # QA was skipped
531- qa_section = "" # Ensure qa_section is empty if QA is skipped
532- if config .SKIP_QA_REVIEW :
533- log ("QA Review was skipped based on SKIP_QA_REVIEW setting." )
534- elif not build_config .has_build_command ():
535- log ("QA Review was skipped as no BUILD_COMMAND was provided." )
536-
537517 # --- Create Pull Request ---
538518 pr_title = git_handler .generate_pr_title (vuln_title )
539519 # Use the result from SmartFix agent remediation as the base PR body.
0 commit comments