testchimp-runner-core 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/execution-service.d.ts +1 -4
  2. package/dist/execution-service.d.ts.map +1 -1
  3. package/dist/execution-service.js +155 -468
  4. package/dist/execution-service.js.map +1 -1
  5. package/dist/index.d.ts +3 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +11 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/llm-facade.d.ts.map +1 -1
  10. package/dist/llm-facade.js +7 -7
  11. package/dist/llm-facade.js.map +1 -1
  12. package/dist/llm-provider.d.ts +9 -0
  13. package/dist/llm-provider.d.ts.map +1 -1
  14. package/dist/model-constants.d.ts +16 -5
  15. package/dist/model-constants.d.ts.map +1 -1
  16. package/dist/model-constants.js +17 -6
  17. package/dist/model-constants.js.map +1 -1
  18. package/dist/orchestrator/decision-parser.d.ts +18 -0
  19. package/dist/orchestrator/decision-parser.d.ts.map +1 -0
  20. package/dist/orchestrator/decision-parser.js +127 -0
  21. package/dist/orchestrator/decision-parser.js.map +1 -0
  22. package/dist/orchestrator/index.d.ts +4 -2
  23. package/dist/orchestrator/index.d.ts.map +1 -1
  24. package/dist/orchestrator/index.js +15 -2
  25. package/dist/orchestrator/index.js.map +1 -1
  26. package/dist/orchestrator/orchestrator-agent.d.ts +17 -22
  27. package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -1
  28. package/dist/orchestrator/orchestrator-agent.js +708 -577
  29. package/dist/orchestrator/orchestrator-agent.js.map +1 -1
  30. package/dist/orchestrator/orchestrator-prompts.d.ts +32 -0
  31. package/dist/orchestrator/orchestrator-prompts.d.ts.map +1 -0
  32. package/dist/orchestrator/orchestrator-prompts.js +737 -0
  33. package/dist/orchestrator/orchestrator-prompts.js.map +1 -0
  34. package/dist/orchestrator/page-som-handler.d.ts +106 -0
  35. package/dist/orchestrator/page-som-handler.d.ts.map +1 -0
  36. package/dist/orchestrator/page-som-handler.js +1353 -0
  37. package/dist/orchestrator/page-som-handler.js.map +1 -0
  38. package/dist/orchestrator/som-types.d.ts +149 -0
  39. package/dist/orchestrator/som-types.d.ts.map +1 -0
  40. package/dist/orchestrator/som-types.js +87 -0
  41. package/dist/orchestrator/som-types.js.map +1 -0
  42. package/dist/orchestrator/tool-registry.d.ts +2 -0
  43. package/dist/orchestrator/tool-registry.d.ts.map +1 -1
  44. package/dist/orchestrator/tool-registry.js.map +1 -1
  45. package/dist/orchestrator/tools/index.d.ts +5 -1
  46. package/dist/orchestrator/tools/index.d.ts.map +1 -1
  47. package/dist/orchestrator/tools/index.js +9 -2
  48. package/dist/orchestrator/tools/index.js.map +1 -1
  49. package/dist/orchestrator/tools/refresh-som-markers.d.ts +12 -0
  50. package/dist/orchestrator/tools/refresh-som-markers.d.ts.map +1 -0
  51. package/dist/orchestrator/tools/refresh-som-markers.js +64 -0
  52. package/dist/orchestrator/tools/refresh-som-markers.js.map +1 -0
  53. package/dist/orchestrator/tools/verify-action-result.d.ts +17 -0
  54. package/dist/orchestrator/tools/verify-action-result.d.ts.map +1 -0
  55. package/dist/orchestrator/tools/verify-action-result.js +140 -0
  56. package/dist/orchestrator/tools/verify-action-result.js.map +1 -0
  57. package/dist/orchestrator/tools/view-previous-screenshot.d.ts +15 -0
  58. package/dist/orchestrator/tools/view-previous-screenshot.d.ts.map +1 -0
  59. package/dist/orchestrator/tools/view-previous-screenshot.js +92 -0
  60. package/dist/orchestrator/tools/view-previous-screenshot.js.map +1 -0
  61. package/dist/orchestrator/types.d.ts +49 -1
  62. package/dist/orchestrator/types.d.ts.map +1 -1
  63. package/dist/orchestrator/types.js +11 -1
  64. package/dist/orchestrator/types.js.map +1 -1
  65. package/dist/prompts.d.ts.map +1 -1
  66. package/dist/prompts.js +40 -34
  67. package/dist/prompts.js.map +1 -1
  68. package/dist/scenario-service.d.ts +5 -0
  69. package/dist/scenario-service.d.ts.map +1 -1
  70. package/dist/scenario-service.js +17 -0
  71. package/dist/scenario-service.js.map +1 -1
  72. package/dist/scenario-worker-class.d.ts +4 -0
  73. package/dist/scenario-worker-class.d.ts.map +1 -1
  74. package/dist/scenario-worker-class.js +21 -3
  75. package/dist/scenario-worker-class.js.map +1 -1
  76. package/dist/testing/agent-tester.d.ts +35 -0
  77. package/dist/testing/agent-tester.d.ts.map +1 -0
  78. package/dist/testing/agent-tester.js +84 -0
  79. package/dist/testing/agent-tester.js.map +1 -0
  80. package/dist/testing/ref-translator-tester.d.ts +44 -0
  81. package/dist/testing/ref-translator-tester.d.ts.map +1 -0
  82. package/dist/testing/ref-translator-tester.js +104 -0
  83. package/dist/testing/ref-translator-tester.js.map +1 -0
  84. package/dist/utils/coordinate-converter.d.ts +32 -0
  85. package/dist/utils/coordinate-converter.d.ts.map +1 -0
  86. package/dist/utils/coordinate-converter.js +130 -0
  87. package/dist/utils/coordinate-converter.js.map +1 -0
  88. package/dist/utils/hierarchical-selector.d.ts +47 -0
  89. package/dist/utils/hierarchical-selector.d.ts.map +1 -0
  90. package/dist/utils/hierarchical-selector.js +212 -0
  91. package/dist/utils/hierarchical-selector.js.map +1 -0
  92. package/dist/utils/page-info-retry.d.ts +14 -0
  93. package/dist/utils/page-info-retry.d.ts.map +1 -0
  94. package/dist/utils/page-info-retry.js +60 -0
  95. package/dist/utils/page-info-retry.js.map +1 -0
  96. package/dist/utils/page-info-utils.d.ts +1 -0
  97. package/dist/utils/page-info-utils.d.ts.map +1 -1
  98. package/dist/utils/page-info-utils.js +46 -18
  99. package/dist/utils/page-info-utils.js.map +1 -1
  100. package/dist/utils/ref-attacher.d.ts +21 -0
  101. package/dist/utils/ref-attacher.d.ts.map +1 -0
  102. package/dist/utils/ref-attacher.js +149 -0
  103. package/dist/utils/ref-attacher.js.map +1 -0
  104. package/dist/utils/ref-translator.d.ts +49 -0
  105. package/dist/utils/ref-translator.d.ts.map +1 -0
  106. package/dist/utils/ref-translator.js +276 -0
  107. package/dist/utils/ref-translator.js.map +1 -0
  108. package/package.json +1 -1
  109. package/plandocs/BEFORE_AFTER_VERIFICATION.md +148 -0
  110. package/plandocs/COORDINATE_MODE_DIAGNOSIS.md +144 -0
  111. package/plandocs/IMPLEMENTATION_STATUS.md +108 -0
  112. package/plandocs/PHASE_1_COMPLETE.md +165 -0
  113. package/plandocs/PHASE_1_SUMMARY.md +184 -0
  114. package/plandocs/PROMPT_OPTIMIZATION_ANALYSIS.md +120 -0
  115. package/plandocs/PROMPT_SANITY_CHECK.md +120 -0
  116. package/plandocs/SESSION_SUMMARY_v0.0.33.md +151 -0
  117. package/plandocs/TROUBLESHOOTING_SESSION.md +72 -0
  118. package/plandocs/VISUAL_AGENT_EVOLUTION_PLAN.md +396 -0
  119. package/plandocs/WHATS_NEW_v0.0.33.md +183 -0
  120. package/plandocs/exploratory-mode-support-v2.plan.md +953 -0
  121. package/plandocs/exploratory-mode-support.plan.md +928 -0
  122. package/plandocs/journey-id-tracking-addendum.md +227 -0
  123. package/src/execution-service.ts +179 -596
  124. package/src/index.ts +10 -0
  125. package/src/llm-facade.ts +8 -8
  126. package/src/llm-provider.ts +11 -1
  127. package/src/model-constants.ts +17 -5
  128. package/src/orchestrator/decision-parser.ts +139 -0
  129. package/src/orchestrator/index.ts +27 -2
  130. package/src/orchestrator/orchestrator-agent.ts +868 -623
  131. package/src/orchestrator/orchestrator-prompts.ts +786 -0
  132. package/src/orchestrator/page-som-handler.ts +1565 -0
  133. package/src/orchestrator/som-types.ts +188 -0
  134. package/src/orchestrator/tool-registry.ts +2 -0
  135. package/src/orchestrator/tools/index.ts +5 -1
  136. package/src/orchestrator/tools/refresh-som-markers.ts +69 -0
  137. package/src/orchestrator/tools/verify-action-result.ts +159 -0
  138. package/src/orchestrator/tools/view-previous-screenshot.ts +103 -0
  139. package/src/orchestrator/types.ts +95 -4
  140. package/src/prompts.ts +40 -34
  141. package/src/scenario-service.ts +20 -0
  142. package/src/scenario-worker-class.ts +30 -4
  143. package/src/utils/coordinate-converter.ts +162 -0
  144. package/src/utils/page-info-retry.ts +65 -0
  145. package/src/utils/page-info-utils.ts +53 -18
  146. package/testchimp-runner-core-0.0.35.tgz +0 -0
  147. /package/{CREDIT_CALLBACK_ARCHITECTURE.md → plandocs/CREDIT_CALLBACK_ARCHITECTURE.md} +0 -0
  148. /package/{INTEGRATION_COMPLETE.md → plandocs/INTEGRATION_COMPLETE.md} +0 -0
  149. /package/{VISION_DIAGNOSTICS_IMPROVEMENTS.md → plandocs/VISION_DIAGNOSTICS_IMPROVEMENTS.md} +0 -0
  150. /package/{RELEASE_0.0.26.md → releasenotes/RELEASE_0.0.26.md} +0 -0
  151. /package/{RELEASE_0.0.27.md → releasenotes/RELEASE_0.0.27.md} +0 -0
  152. /package/{RELEASE_0.0.28.md → releasenotes/RELEASE_0.0.28.md} +0 -0
@@ -21,6 +21,8 @@ import { DEFAULT_MODEL, VISION_MODEL } from './model-constants';
21
21
  import { LLMProvider } from './llm-provider';
22
22
  import { ProgressReporter } from './progress-reporter';
23
23
  import { BackendProxyLLMProvider } from './providers/backend-proxy-llm-provider';
24
+ import { OrchestratorAgent, ToolRegistry, DEFAULT_AGENT_CONFIG } from './orchestrator';
25
+ import type { AgentConfig, JourneyMemory } from './orchestrator';
24
26
 
25
27
  /**
26
28
  * Service for orchestrating Playwright script execution
@@ -34,6 +36,7 @@ export class ExecutionService {
34
36
  private maxConcurrentExecutions: number;
35
37
  private activeExecutions: Set<Promise<any>> = new Set();
36
38
  private logger?: (message: string, level?: 'log' | 'error' | 'warn') => void;
39
+ private orchestratorAgent: OrchestratorAgent;
37
40
 
38
41
  constructor(
39
42
  authConfig?: AuthConfig,
@@ -51,6 +54,21 @@ export class ExecutionService {
51
54
  this.progressReporter = progressReporter;
52
55
  this.creditUsageService = new CreditUsageService(authConfig, backendUrl);
53
56
  this.maxConcurrentExecutions = maxConcurrentExecutions;
57
+
58
+ // Initialize orchestrator for repair mode (reuses all SoM infrastructure)
59
+ const toolRegistry = new ToolRegistry();
60
+ const repairConfig: Partial<AgentConfig> = {
61
+ useSoM: true,
62
+ somRestrictCoordinates: true // Prefer SoM markers for repairs
63
+ };
64
+
65
+ this.orchestratorAgent = new OrchestratorAgent(
66
+ this.llmFacade,
67
+ toolRegistry,
68
+ repairConfig,
69
+ progressReporter,
70
+ (msg, level) => this.log(msg)
71
+ );
54
72
  }
55
73
 
56
74
  /**
@@ -253,7 +271,7 @@ export class ExecutionService {
253
271
  }
254
272
 
255
273
  // Execute the script as-is
256
- await this.executeScriptContent(request.script, page);
274
+ await this.executeStepCode(request.script, page);
257
275
 
258
276
  // LIFECYCLE: Call afterEndTest on success
259
277
  if (this.progressReporter?.afterEndTest) {
@@ -301,7 +319,7 @@ export class ExecutionService {
301
319
  }
302
320
 
303
321
  // Execute the script as-is
304
- await this.executeScriptContent(request.script, page);
322
+ await this.executeStepCode(request.script, page);
305
323
 
306
324
  // LIFECYCLE: Call afterEndTest on success
307
325
  if (this.progressReporter?.afterEndTest) {
@@ -385,8 +403,12 @@ export class ExecutionService {
385
403
  // Start AI repair process
386
404
  this.log('Starting AI repair process...');
387
405
 
406
+ let repairBrowser: any = null;
407
+ let repairContext: any = null;
408
+ let repairPage: any = null;
409
+
388
410
  try {
389
- let repairBrowser, repairContext, repairPage, steps, updatedSteps;
411
+ let steps, updatedSteps;
390
412
 
391
413
  if (useExistingBrowser) {
392
414
  // Use existing browser
@@ -451,8 +473,8 @@ export class ExecutionService {
451
473
  updatedSteps = await this.repairStepsWithAI(steps, repairPage, repairFlexibility, model, request.jobId);
452
474
  }
453
475
 
454
- // Always generate the updated script
455
- const updatedScript = this.generateUpdatedScript(updatedSteps);
476
+ // Always generate the updated script (preserve original test name)
477
+ const updatedScript = this.generateUpdatedScript(updatedSteps, undefined, request.script);
456
478
 
457
479
  // Check if repair was successful by seeing if we completed all steps
458
480
  const allStepsSuccessful = updatedSteps.length > 0 && updatedSteps.every(step => step.success);
@@ -472,11 +494,32 @@ export class ExecutionService {
472
494
 
473
495
  // Update file if we have any successful repairs (partial or complete)
474
496
  if (hasSuccessfulRepairs) {
497
+ // IMPORTANT: Use the orchestrator-generated script directly (already has proper Playwright commands)
498
+ // Don't regenerate via LLM as it loses the actual repairs
499
+ this.log('Using orchestrator-generated script (skipping LLM regeneration to preserve repairs)');
500
+
501
+ // For repair advice, compare original vs repaired
475
502
  const confidenceResponse = await this.llmFacade.assessRepairConfidence(request.script!, updatedScript, model);
476
- const finalScript = await this.llmFacade.generateFinalScript(request.script!, updatedScript, confidenceResponse.advice, model);
477
503
 
478
- // Ensure the final script has the correct TestChimp comment format with repair advice
479
- const scriptWithRepairAdvice = addTestChimpComment(finalScript, confidenceResponse.advice);
504
+ // Add TestChimp comment with repair advice
505
+ const scriptWithAdvice = addTestChimpComment(updatedScript, confidenceResponse.advice);
506
+
507
+ // Polish the script with minor LLM cleanup (removes redundancies, fixes formatting)
508
+ this.log('Applying final LLM polish to repaired script (minor cleanup only)...');
509
+ const cleanupResult = await this.llmFacade.cleanupScript(scriptWithAdvice, model);
510
+
511
+ if (cleanupResult.changes.length > 0) {
512
+ this.log(`Script cleanup made ${cleanupResult.changes.length} minor improvements:`);
513
+ cleanupResult.changes.forEach((change, i) => {
514
+ this.log(` ${i + 1}. ${change}`);
515
+ });
516
+ } else if (cleanupResult.skipped) {
517
+ this.log(`Script cleanup skipped: ${cleanupResult.skipped}`);
518
+ } else {
519
+ this.log('Script cleanup: no changes needed');
520
+ }
521
+
522
+ const scriptWithRepairAdvice = cleanupResult.script;
480
523
 
481
524
  // Report credit usage for successful AI repair
482
525
  this.creditUsageService.reportAIRepairCredit().catch(error => {
@@ -496,11 +539,6 @@ export class ExecutionService {
496
539
  }
497
540
  }
498
541
 
499
- // Only close browser if we created it (not provided by caller)
500
- if (!useExistingBrowser) {
501
- await repairBrowser.close();
502
- }
503
-
504
542
  return {
505
543
  runStatus: 'failed', // Original script failed
506
544
  repairStatus: allStepsSuccessful ? 'success' : 'partial', // Complete or partial repair success
@@ -522,11 +560,6 @@ export class ExecutionService {
522
560
  }
523
561
  }
524
562
 
525
- // Only close browser if we created it (not provided by caller)
526
- if (!useExistingBrowser) {
527
- await repairBrowser.close();
528
- }
529
-
530
563
  return {
531
564
  runStatus: 'failed', // Original script failed
532
565
  repairStatus: 'failed',
@@ -546,6 +579,16 @@ export class ExecutionService {
546
579
  executionTime: Date.now() - startTime,
547
580
  error: error instanceof Error ? error.message : 'Script execution failed'
548
581
  };
582
+ } finally {
583
+ // Clean up browser resources if we created them (not provided by caller)
584
+ if (!useExistingBrowser && repairBrowser) {
585
+ try {
586
+ await repairBrowser.close();
587
+ this.log('AI repair browser closed');
588
+ } catch (closeError) {
589
+ this.log(`Error closing AI repair browser: ${closeError}`, 'warn');
590
+ }
591
+ }
549
592
  }
550
593
  }
551
594
 
@@ -629,6 +672,9 @@ export class ExecutionService {
629
672
  newCode?: string;
630
673
  }> = [];
631
674
 
675
+ // Track actual executed steps (including agent repairs) for proper history
676
+ const executedStepDescriptions: string[] = [];
677
+
632
678
  // Create a shared execution context that accumulates all executed code for variable tracking
633
679
  let executionContext = '';
634
680
  const contextVariables = new Map<string, any>();
@@ -660,6 +706,9 @@ export class ExecutionService {
660
706
  this.log(`Step ${i + 1} executed successfully: ${step.description}`);
661
707
  this.log(`Step ${i + 1} success status set to: ${step.success}`);
662
708
 
709
+ // Track executed step description for agent context
710
+ executedStepDescriptions.push(step.description);
711
+
663
712
  // Report successful step execution
664
713
  this.log(`DEBUG: About to check callback - progressReporter=${!!this.progressReporter}, onStepProgress=${!!this.progressReporter?.onStepProgress}, jobId=${jobId}`);
665
714
  if (this.progressReporter?.onStepProgress && jobId) {
@@ -691,304 +740,114 @@ export class ExecutionService {
691
740
  step.success = false;
692
741
  step.error = this.safeSerializeError(error);
693
742
 
694
- // Try multiple repair attempts
695
- const repairHistory: Array<{
696
- attempt: number;
697
- action: StepRepairAction;
698
- error: string;
699
- pageInfo: PageInfo;
700
- }> = [];
701
-
743
+ // Use orchestrator for repair (reuses all SoM infrastructure)
744
+ this.log(`Calling orchestrator in REPAIR mode for step ${i + 1}`);
745
+
746
+ // Prepare repair context - use executedStepDescriptions (includes agent repairs)
747
+ const priorSteps = executedStepDescriptions; // What was ACTUALLY executed (scripted + agent)
748
+ const nextSteps = updatedSteps.slice(i + 1).map(s => s.description);
749
+
750
+ this.log(` Prior steps executed: ${priorSteps.length}, Next steps: ${nextSteps.length}`);
751
+ this.log(` Prior steps context:\n ${priorSteps.map((s, idx) => `${idx + 1}. ${s}`).join('\n ')}`);
752
+
753
+ // Create minimal memory for repair
754
+ const memory: JourneyMemory = {
755
+ experiences: [],
756
+ extractedData: {},
757
+ history: [],
758
+ latestNote: undefined
759
+ };
760
+
702
761
  let repairSuccess = false;
703
- const originalDescription = step.description;
704
- const originalCode = step.code;
705
- let usedVisionMode = false;
706
762
 
707
- for (let attempt = 1; attempt <= maxTries; attempt++) {
708
- this.log(`Step ${i + 1} repair attempt ${attempt}/${maxTries}`);
709
-
710
- // Get current page state for AI repair
711
- const pageInfo = await this.getEnhancedPageInfo(page);
712
-
713
- // Build failure history for LLM context
714
- const failureHistory = this.buildFailureHistory(repairHistory, step, error);
715
-
716
- // Build recent repairs context for LLM
717
- const recentRepairsContext = this.buildRecentRepairsContext(recentRepairs);
718
-
719
- let repairSuggestion;
763
+ try {
764
+ // Call orchestrator with repair context (page object persisted)
765
+ const repairResult = await this.orchestratorAgent.executeStep(
766
+ page, // Same page object (persisted state)
767
+ step.description, // Goal with testdata embedded
768
+ i + 1, // Current step number
769
+ updatedSteps.length, // Total steps
770
+ updatedSteps.map(s => s.description), // All step descriptions
771
+ memory, // Memory (empty for repair)
772
+ jobId || 'repair',
773
+ priorSteps, // NEW: What was already completed
774
+ nextSteps // NEW: What comes after this
775
+ );
720
776
 
721
- // VISION-BASED FALLBACK: After 2 regular repair attempts, consider vision diagnostics on final attempt
722
- if (attempt === maxTries - 1 && repairHistory.length >= 2 && !usedVisionMode) {
723
- // Ask LLM if screenshot would help for repair diagnostics
724
- this.log(` 🤔 After ${repairHistory.length} failed repairs: Asking LLM if screenshot would help (last resort)...`);
725
-
726
- const screenshotNeed = await this.llmFacade.assessScreenshotNeed(
727
- step.description,
728
- step.error || 'Unknown error',
729
- repairHistory.length + 1,
730
- pageInfo,
731
- model
732
- );
777
+ if (repairResult.success && repairResult.commands.length > 0) {
778
+ // MODIFY: Orchestrator fixed the step - replace with new code
779
+ const repairedCode = repairResult.commands.join('\n');
733
780
 
734
- this.log(` 💭 LLM assessment: ${screenshotNeed.needsScreenshot ? 'SCREENSHOT NEEDED' : 'NO SCREENSHOT'} - ${screenshotNeed.reason}`);
735
-
736
- if (screenshotNeed.needsScreenshot) {
737
- // Two-step supervisor pattern for vision-based repair:
738
- // 1. Supervisor analyzes screenshot and provides diagnostic insights
739
- // 2. Get repair suggestion with enhanced context from vision analysis
740
-
741
- this.log(` 📸 Taking screenshot for supervisor analysis...`);
742
-
743
- // Capture optimized screenshot using utility method
744
- const imageDataUrl = await captureOptimizedScreenshot(
745
- page,
746
- { timeout: 10000 }, // Uses default quality 60
747
- (msg) => this.log(msg)
748
- );
749
-
750
- this.log(` 👔 STEP 1: Supervisor analyzing screenshot (${VISION_MODEL})...`);
751
- const supervisorDiagnostics = await this.llmFacade.getVisionDiagnostics(
752
- step.description,
753
- pageInfo,
754
- [], // No previous steps context for repair
755
- step.error,
756
- imageDataUrl,
757
- VISION_MODEL
758
- );
759
-
760
- // DEBUG: Log vision diagnostics
761
- this.log(` 📸 Visual insights: ${supervisorDiagnostics.visualAnalysis}`);
762
- this.log(` 🔍 Root cause: ${supervisorDiagnostics.rootCause}`);
763
- this.log(` 💡 Recommended approach: ${supervisorDiagnostics.recommendedApproach}`);
764
- if (supervisorDiagnostics.elementsFound.length > 0) {
765
- this.log(` ✅ Elements found: ${supervisorDiagnostics.elementsFound.join(', ')}`);
766
- }
767
- if (supervisorDiagnostics.elementsNotFound.length > 0) {
768
- this.log(` ❌ Elements not found: ${supervisorDiagnostics.elementsNotFound.join(', ')}`);
769
- }
770
-
771
- // Get repair suggestion with vision-enhanced context
772
- this.log(` 🔨 STEP 2: Getting repair suggestion with vision insights...`);
773
- const visionEnhancedFailureHistory = `${failureHistory}
774
-
775
- VISION-BASED DIAGNOSTIC INSIGHTS:
776
- Visual Analysis: ${supervisorDiagnostics.visualAnalysis}
777
- Root Cause: ${supervisorDiagnostics.rootCause}
778
- Recommended Approach: ${supervisorDiagnostics.recommendedApproach}
779
- Elements Found: ${supervisorDiagnostics.elementsFound.join(', ') || 'None'}
780
- Elements Not Found: ${supervisorDiagnostics.elementsNotFound.join(', ') || 'None'}
781
-
782
- Use these vision insights to inform your repair strategy.`;
783
-
784
- repairSuggestion = await this.llmFacade.getRepairSuggestion(
785
- step.description,
786
- step.code,
787
- step.error || 'Unknown error',
788
- pageInfo,
789
- visionEnhancedFailureHistory,
790
- recentRepairsContext,
791
- model
792
- );
793
-
794
- usedVisionMode = true;
795
- } else {
796
- // Regular repair without vision
797
- if (screenshotNeed.alternativeApproach) {
798
- this.log(` 💡 Alternative approach: ${screenshotNeed.alternativeApproach}`);
799
- }
800
- repairSuggestion = await this.llmFacade.getRepairSuggestion(
801
- step.description,
802
- step.code,
803
- step.error || 'Unknown error',
804
- pageInfo,
805
- failureHistory,
806
- recentRepairsContext,
807
- model
808
- );
809
- }
810
- } else {
811
- // Regular repair attempt (first 2 attempts or already used vision)
812
- repairSuggestion = await this.llmFacade.getRepairSuggestion(
813
- step.description,
814
- step.code,
815
- step.error || 'Unknown error',
816
- pageInfo,
817
- failureHistory,
818
- recentRepairsContext,
819
- model
820
- );
821
- }
822
-
823
- if (!repairSuggestion.shouldContinue) {
824
- this.log(`AI decided to stop repair at attempt ${attempt}: ${repairSuggestion.reason}`);
825
- break;
826
- }
827
-
828
- // Apply the repair action
829
- try {
830
- // Set the step index and insertAfterIndex on the client side based on current step being processed
831
- const repairAction = {
832
- ...repairSuggestion.action,
833
- stepIndex: i, // Client-side step index management
834
- insertAfterIndex: repairSuggestion.action.operation === StepOperation.INSERT ? i - 1 : undefined // For INSERT, insert before current step
781
+ updatedSteps[i] = {
782
+ ...step,
783
+ code: repairedCode,
784
+ success: true,
785
+ error: undefined
835
786
  };
836
787
 
837
- this.log(`🔧 Applying repair action: ${repairAction.operation} on step ${repairAction.stepIndex}`);
838
- this.log(`🔧 Steps array before repair: ${updatedSteps.map((s, idx) => `${idx}: "${s.description}" (success: ${s.success})`).join(', ')}`);
788
+ this.log(`✓ Step ${i + 1} MODIFIED by orchestrator (repair successful)`);
789
+ this.log(` Original code: ${step.code}`);
790
+ this.log(` New code (${repairResult.commands.length} commands):\n ${repairResult.commands.join('\n ')}`);
839
791
 
840
- const result = await this.applyRepairActionInContext(repairAction, updatedSteps, i, page, executionContext, contextVariables);
792
+ // Track what agent actually did in history (for future repair context)
793
+ const agentActionSummary = `${step.description} [AI-repaired: ${repairResult.commands.length} commands]`;
794
+ executedStepDescriptions.push(agentActionSummary);
841
795
 
842
- if (result.success) {
843
- repairSuccess = true;
844
- this.log(`🔧 Steps array after repair: ${updatedSteps.map((s, idx) => `${idx}: "${s.description}" (success: ${s.success})`).join(', ')}`);
845
-
846
- // Mark the appropriate step(s) as successful based on operation type
847
- if (repairAction.operation === StepOperation.MODIFY) {
848
- // For MODIFY: mark the modified step as successful
849
- step.success = true;
850
- step.error = undefined;
851
- updatedSteps[i].success = true;
852
- updatedSteps[i].error = undefined;
853
- this.log(`Step ${i + 1} marked as successful after MODIFY repair`);
854
-
855
- // Report repaired step
856
- if (this.progressReporter?.onStepProgress && jobId) {
857
- this.log(`DEBUG: Reporting repaired step ${i + 1}:`);
858
- this.log(` description: ${updatedSteps[i].description}`);
859
- this.log(` code: ${updatedSteps[i].code}`);
860
- await this.progressReporter.onStepProgress({
861
- jobId,
862
- stepId: updatedSteps[i].id, // Preserve original step ID if provided
863
- stepNumber: i + 1,
864
- description: updatedSteps[i].description,
865
- code: updatedSteps[i].code,
866
- status: 'SUCCESS_STEP_EXECUTION' as any,
867
- wasRepaired: true
868
- });
869
- }
870
- } else if (repairAction.operation === StepOperation.INSERT) {
871
- // For INSERT: mark the newly inserted step as successful
872
- const insertIndex = repairAction.insertAfterIndex !== undefined ? repairAction.insertAfterIndex + 1 : i + 1;
873
- if (updatedSteps[insertIndex]) {
874
- updatedSteps[insertIndex].success = true;
875
- updatedSteps[insertIndex].error = undefined;
876
-
877
- // Report inserted step
878
- if (this.progressReporter?.onStepProgress && jobId) {
879
- await this.progressReporter.onStepProgress({
880
- jobId,
881
- stepId: updatedSteps[insertIndex].id, // Preserve original step ID if provided
882
- stepNumber: insertIndex + 1,
883
- description: updatedSteps[insertIndex].description,
884
- code: updatedSteps[insertIndex].code,
885
- status: 'SUCCESS_STEP_EXECUTION' as any,
886
- wasRepaired: true
887
- });
888
- }
889
- }
890
- } else if (repairAction.operation === StepOperation.REMOVE) {
891
- // For REMOVE: no step to mark as successful since we removed it
892
- // The step is already removed from the array
893
- }
894
-
895
- const commandInfo = repairAction.operation === StepOperation.MODIFY ?
896
- `MODIFY: "${repairAction.newStep?.code || 'N/A'}"` :
897
- repairAction.operation === StepOperation.INSERT ?
898
- `INSERT: "${repairAction.newStep?.code || 'N/A'}"` :
899
- repairAction.operation === StepOperation.REMOVE ?
900
- `REMOVE: step at index ${repairAction.stepIndex}` :
901
- repairAction.operation;
902
- this.log(`Step ${i + 1} repair action ${commandInfo} executed successfully on attempt ${attempt}${usedVisionMode ? ' (vision-aided)' : ''}`);
903
-
904
- // Update execution context based on the repair action
905
- if (repairAction.operation === StepOperation.MODIFY && repairAction.newStep) {
906
- // Update the step in the execution context for variable tracking
907
- executionContext = executionContext.replace(originalCode, repairAction.newStep.code);
908
- } else if (repairAction.operation === StepOperation.INSERT && repairAction.newStep) {
909
- // Insert the new step code into execution context for variable tracking
910
- executionContext += repairAction.newStep.code + '\n';
911
- } else if (repairAction.operation === StepOperation.REMOVE) {
912
- // Remove the step code from execution context for variable tracking
913
- executionContext = executionContext.replace(originalCode, '');
914
- }
915
-
916
- // Record this successful repair
917
- recentRepairs.push({
796
+ // Report repaired step
797
+ if (this.progressReporter?.onStepProgress && jobId) {
798
+ await this.progressReporter.onStepProgress({
799
+ jobId,
800
+ stepId: step.id,
918
801
  stepNumber: i + 1,
919
- operation: repairAction.operation,
920
- originalDescription: repairAction.operation === StepOperation.REMOVE ? originalDescription : undefined,
921
- newDescription: repairAction.newStep?.description,
922
- originalCode: repairAction.operation === StepOperation.REMOVE ? originalCode : undefined,
923
- newCode: repairAction.newStep?.code
802
+ description: updatedSteps[i].description,
803
+ code: updatedSteps[i].code,
804
+ status: 'SUCCESS_STEP_EXECUTION' as any,
805
+ wasRepaired: true
924
806
  });
925
-
926
- // Keep only the last 3 repairs for context
927
- if (recentRepairs.length > 3) {
928
- recentRepairs.shift();
929
- }
930
-
931
- // Update step index based on operation
932
- if (repairAction.operation === StepOperation.INSERT) {
933
- // For INSERT: inserted step is already executed
934
- this.log(`INSERT operation: current i=${i}, insertAfterIndex=${repairAction.insertAfterIndex}`);
935
- this.log(`INSERT: Steps array length before: ${updatedSteps.length}`);
936
- this.log(`INSERT: Steps before operation: ${updatedSteps.map((s, idx) => `${idx}: "${s.description}" (success: ${s.success})`).join(', ')}`);
937
-
938
- if (repairAction.insertAfterIndex !== undefined && repairAction.insertAfterIndex < i) {
939
- // If inserting before current position, current step moved down by 1
940
- this.log(`INSERT before current position: incrementing i from ${i} to ${i + 1}`);
941
- i++; // Move to the original step that was pushed to the next position
942
- } else {
943
- // If inserting at or after current position, stay at current step
944
- this.log(`INSERT at/after current position: keeping i at ${i}`);
945
- }
946
-
947
- this.log(`INSERT: Steps array length after: ${updatedSteps.length}`);
948
- this.log(`INSERT: Steps after operation: ${updatedSteps.map((s, idx) => `${idx}: "${s.description}" (success: ${s.success})`).join(', ')}`);
949
- } else if (repairAction.operation === StepOperation.REMOVE) {
950
- // For REMOVE: stay at same index since the next step moved to current position
951
- // Don't increment i because the array shifted left
952
- } else {
953
- // For MODIFY: move to next step since modified step was executed
954
- i++; // Move to next step for MODIFY
955
- }
956
-
957
- // Add the repaired step's code to execution context for variable tracking
958
- executionContext += step.code + '\n';
959
-
960
- break;
961
- } else {
962
- throw new Error(result.error || 'Repair action failed');
963
807
  }
964
- } catch (repairError) {
965
- const repairErrorMessage = repairError instanceof Error ? repairError.message : 'Repair failed';
966
- const commandInfo = repairSuggestion.action.operation === StepOperation.MODIFY ?
967
- `MODIFY: "${repairSuggestion.action.newStep?.code || 'N/A'}"` :
968
- repairSuggestion.action.operation === StepOperation.INSERT ?
969
- `INSERT: "${repairSuggestion.action.newStep?.code || 'N/A'}"` :
970
- repairSuggestion.action.operation === StepOperation.REMOVE ?
971
- `REMOVE: step at index ${repairSuggestion.action.stepIndex}` :
972
- repairSuggestion.action.operation;
973
- this.log(`Step ${i + 1} repair attempt ${attempt} failed (${commandInfo}): ${repairErrorMessage}`);
974
- if (repairError instanceof Error && repairError.stack) {
975
- this.log(` Repair stack trace: ${repairError.stack}`);
808
+
809
+ // Ensure page is stable after agent repairs before returning control to script
810
+ this.log(`Waiting for page stability after agent repair...`);
811
+ try {
812
+ await page.waitForLoadState('networkidle', { timeout: 5000 });
813
+ this.log(`Page stabilized (networkidle) after agent repair`);
814
+ } catch (stabilityError) {
815
+ try {
816
+ await page.waitForLoadState('domcontentloaded', { timeout: 3000 });
817
+ this.log(`Page loaded (domcontentloaded) after agent repair`);
818
+ } catch (fallbackError) {
819
+ this.log(`Page stability wait timed out (continuing anyway)`, 'warn');
820
+ }
976
821
  }
977
822
 
978
- // Record this attempt in history
979
- repairHistory.push({
980
- attempt,
981
- action: repairSuggestion.action,
982
- error: repairErrorMessage,
983
- pageInfo
984
- });
823
+ repairSuccess = true;
824
+ i++; // Continue to NEXT step (hand control back to script)
825
+
826
+ } else if (repairResult.success && repairResult.commands.length === 0) {
827
+ // DELETE: Step goal already achieved or no longer needed (e.g., modal already dismissed)
828
+ this.log(`✓ Step ${i + 1} DELETED by orchestrator (goal already achieved, step obsolete)`);
829
+ this.log(` Reason: Orchestrator completed with 0 commands - step no longer needed`);
830
+
831
+ // Track deletion in history (helps agent understand what was skipped)
832
+ executedStepDescriptions.push(`${step.description} [AI-deleted: step obsolete/already done]`);
833
+
834
+ // Remove the step from array
835
+ updatedSteps.splice(i, 1);
836
+
837
+ repairSuccess = true;
838
+ // Don't increment i - next step moved to current position
985
839
 
986
- step.error = repairErrorMessage;
840
+ } else {
841
+ this.log(`✗ Step ${i + 1} could not be repaired by orchestrator (reason: ${repairResult.terminationReason})`);
987
842
  }
843
+ } catch (repairError: any) {
844
+ this.log(`✗ Orchestrator repair failed: ${repairError.message}`);
988
845
  }
989
-
846
+
847
+ // Legacy repair code removed - now using orchestrator
848
+
990
849
  if (!repairSuccess) {
991
- this.log(`Step ${i + 1} failed after ${maxTries} repair attempts`);
850
+ this.log(`Step ${i + 1} could not be repaired - stopping execution`);
992
851
  break;
993
852
  }
994
853
  }
@@ -1041,310 +900,34 @@ Use these vision insights to inform your repair strategy.`;
1041
900
  return code; // Return the original code without removing comments
1042
901
  }
1043
902
 
1044
- private async executeStepInContext(
1045
- code: string,
1046
- page: any,
1047
- executionContext: string,
1048
- contextVariables: Map<string, any>
1049
- ): Promise<void> {
1050
- // Detect if code contains navigation or load state operations that need longer timeout
1051
- const needsLongerTimeout = code.includes('waitForLoadState') ||
1052
- code.includes('goto(') ||
1053
- code.includes('waitForURL') ||
1054
- code.includes('waitForNavigation');
1055
-
1056
- // Use appropriate timeout based on operation type
1057
- const timeout = needsLongerTimeout ? 30000 : 5000;
1058
- page.setDefaultTimeout(timeout);
1059
-
1060
- try {
1061
- // Execute only the current step code, but make context variables available
1062
- const fullCode = code;
1063
-
1064
- // Dynamically import expect
1065
- const { expect } = require('@playwright/test');
1066
-
1067
- // Create a function that has access to page, expect, and the context variables
1068
- const executeCode = new Function(
1069
- 'page',
1070
- 'expect',
1071
- 'contextVariables',
1072
- `return (async () => {
1073
- // Make context variables available in the execution scope
1074
- for (const [key, value] of contextVariables) {
1075
- globalThis[key] = value;
1076
- }
1077
-
1078
- ${fullCode}
1079
-
1080
- // Capture any new variables that might have been created
1081
- const newVars = {};
1082
- for (const key in globalThis) {
1083
- if (!contextVariables.has(key) && typeof globalThis[key] !== 'function' && key !== 'page' && key !== 'expect') {
1084
- newVars[key] = globalThis[key];
1085
- }
1086
- }
1087
- return newVars;
1088
- })()`
1089
- );
1090
-
1091
- const newVars = await executeCode(page, expect, contextVariables);
1092
-
1093
- // Update the context variables with any new variables created
1094
- for (const [key, value] of Object.entries(newVars)) {
1095
- contextVariables.set(key, value);
1096
- }
1097
- } finally {
1098
- // Reset to default timeout for element operations
1099
- page.setDefaultTimeout(5000);
1100
- }
1101
- }
1102
-
1103
- private async executeScriptContent(script: string, page: any): Promise<void> {
1104
- // Extract the test function content
1105
- const testMatch = script.match(/test\([^,]+,\s*async\s*\(\s*\{\s*page[^}]*\}\s*\)\s*=>\s*\{([\s\S]*)\}\s*\);/);
1106
- if (!testMatch) {
1107
- throw new Error('Could not extract test function from script');
1108
- }
1109
-
1110
- const testBody = testMatch[1];
1111
-
1112
- // Dynamically import expect
1113
- const { expect } = require('@playwright/test');
1114
-
1115
- // Execute the entire test body as one async function
1116
- const executeTest = new Function('page', 'expect', `return (async () => { ${testBody} })()`);
1117
- await executeTest(page, expect);
1118
- }
1119
-
1120
- private async getEnhancedPageInfo(page: any): Promise<PageInfo> {
1121
- try {
1122
- return await getEnhancedPageInfo(page);
1123
- } catch (error) {
1124
- return {
1125
- url: page.url(),
1126
- title: 'Unknown',
1127
- ariaSnapshot: null,
1128
- interactiveElements: [],
1129
- formattedElements: 'Unable to extract'
1130
- };
1131
- }
1132
- }
1133
-
1134
- private buildFailureHistory(
1135
- repairHistory: Array<{ attempt: number; action: StepRepairAction; error: string; pageInfo: PageInfo }>,
1136
- originalStep: ScriptStep,
1137
- originalError: any
1138
- ): string {
1139
- if (repairHistory.length === 0) {
1140
- return `Original failure: ${this.safeSerializeError(originalError)}`;
1141
- }
1142
-
1143
- let history = `Original failure: ${this.safeSerializeError(originalError)}\n\n`;
1144
- history += `Previous repair attempts:\n`;
1145
-
1146
- repairHistory.forEach((attempt, index) => {
1147
- history += `Attempt ${attempt.attempt}:\n`;
1148
- history += ` Operation: ${attempt.action.operation}\n`;
1149
- if (attempt.action.newStep) {
1150
- history += ` Description: ${attempt.action.newStep.description}\n`;
1151
- history += ` Code: ${attempt.action.newStep.code}\n`;
1152
- }
1153
- history += ` Error: ${attempt.error}\n`;
1154
- if (index < repairHistory.length - 1) {
1155
- history += `\n`;
1156
- }
1157
- });
1158
-
1159
- return history;
903
+ // Legacy repair helper methods (now unused but kept for compilation)
904
+ private buildFailureHistory(): string { return ''; }
905
+ private buildRecentRepairsContext(): string { return ''; }
906
+ private async applyRepairActionInContext(): Promise<{ success: boolean; error?: string }> {
907
+ return { success: false };
1160
908
  }
1161
909
 
1162
- private buildRecentRepairsContext(
1163
- recentRepairs: Array<{
1164
- stepNumber: number;
1165
- operation: string;
1166
- originalDescription?: string;
1167
- newDescription?: string;
1168
- originalCode?: string;
1169
- newCode?: string;
1170
- }>
1171
- ): string {
1172
- if (recentRepairs.length === 0) {
1173
- return 'No recent repairs to consider.';
1174
- }
1175
-
1176
- let context = 'Recent successful repairs that may affect this step:\n\n';
910
+ private generateUpdatedScript(steps: (ScriptStep & { success?: boolean; error?: string })[], repairAdvice?: string, originalScript?: string): string {
911
+ // Extract test name and hashtags from original script if provided
912
+ let testName = 'repairedTest';
913
+ let hashtags: string[] = [];
1177
914
 
1178
- recentRepairs.forEach((repair, index) => {
1179
- context += `Step ${repair.stepNumber} was successfully repaired:\n`;
1180
- context += ` Operation: ${repair.operation}\n`;
1181
-
1182
- if (repair.operation === 'REMOVE') {
1183
- context += ` Removed: "${repair.originalDescription}"\n`;
1184
- context += ` Code removed:\n ${repair.originalCode?.replace(/\n/g, '\n ')}\n`;
1185
- } else if (repair.operation === 'INSERT') {
1186
- context += ` Inserted: "${repair.newDescription}"\n`;
1187
- context += ` Code inserted:\n ${repair.newCode?.replace(/\n/g, '\n ')}\n`;
1188
- } else {
1189
- context += ` Original: "${repair.originalDescription}"\n`;
1190
- context += ` Repaired: "${repair.newDescription}"\n`;
1191
- context += ` Code changed from:\n ${repair.originalCode?.replace(/\n/g, '\n ')}\n`;
1192
- context += ` To:\n ${repair.newCode?.replace(/\n/g, '\n ')}\n`;
915
+ if (originalScript) {
916
+ const testNameMatch = originalScript.match(/test\(['"]([^'"]+)['"]/);
917
+ if (testNameMatch) {
918
+ testName = testNameMatch[1];
1193
919
  }
1194
920
 
1195
- if (index < recentRepairs.length - 1) {
1196
- context += `\n`;
921
+ // Extract hashtags from TestChimp comment
922
+ const hashtagMatch = originalScript.match(/#\w+(?:\s+#\w+)*/);
923
+ if (hashtagMatch) {
924
+ hashtags = hashtagMatch[0].split(/\s+/).filter(tag => tag.startsWith('#'));
1197
925
  }
1198
- });
1199
-
1200
- context += '\nConsider how these changes might affect the current step and adjust accordingly.';
1201
- return context;
1202
- }
1203
-
1204
- private async applyRepairActionInContext(
1205
- action: StepRepairAction,
1206
- steps: (ScriptStep & { success?: boolean; error?: string })[],
1207
- currentIndex: number,
1208
- page: any,
1209
- executionContext: string,
1210
- contextVariables: Map<string, any>
1211
- ): Promise<{ success: boolean; error?: string; updatedContext?: string }> {
1212
- try {
1213
- switch (action.operation) {
1214
- case StepOperation.MODIFY:
1215
- if (action.newStep && action.stepIndex !== undefined) {
1216
- // Modify existing step
1217
- steps[action.stepIndex] = {
1218
- ...action.newStep,
1219
- success: false,
1220
- error: undefined
1221
- };
1222
- // Test the modified step with current page state and variables
1223
- await this.executeStepCode(action.newStep.code, page);
1224
- return { success: true, updatedContext: executionContext + action.newStep.code };
1225
- }
1226
- break;
1227
-
1228
- case StepOperation.INSERT:
1229
- if (action.newStep && action.insertAfterIndex !== undefined) {
1230
- // Insert new step after specified index
1231
- const insertIndex = action.insertAfterIndex + 1;
1232
- const newStep = {
1233
- ...action.newStep,
1234
- success: false,
1235
- error: undefined
1236
- };
1237
- this.log(`INSERT: Inserting step at index ${insertIndex} with description "${newStep.description}"`);
1238
- this.log(`INSERT: Steps before insertion: ${steps.map((s, i) => `${i}: "${s.description}" (success: ${s.success})`).join(', ')}`);
1239
-
1240
- // Preserve success status of existing steps before insertion
1241
- const successStatusMap = new Map(steps.map((step, index) => [index, { success: step.success, error: step.error }]));
1242
-
1243
- steps.splice(insertIndex, 0, newStep);
1244
-
1245
- // Restore success status for steps that were shifted by the insertion
1246
- // Steps at insertIndex and before keep their original status
1247
- // Steps after insertIndex need to be shifted to their new positions
1248
- for (let i = insertIndex + 1; i < steps.length; i++) {
1249
- const originalIndex = i - 1; // The step that was originally at this position
1250
- if (successStatusMap.has(originalIndex)) {
1251
- const status = successStatusMap.get(originalIndex)!;
1252
- steps[i].success = status.success;
1253
- steps[i].error = status.error;
1254
- }
1255
- }
1256
-
1257
- // CRITICAL FIX: Ensure the inserted step doesn't overwrite existing step data
1258
- // The new step should only have its own description, not inherit from existing steps
1259
- this.log(`INSERT: Final step array after restoration: ${steps.map((s, i) => `${i}: "${s.description}" (success: ${s.success})`).join(', ')}`);
1260
-
1261
- this.log(`INSERT: Steps after insertion: ${steps.map((s, i) => `${i}: "${s.description}" (success: ${s.success})`).join(', ')}`);
1262
- // Test the new step with current page state
1263
- await this.executeStepCode(action.newStep.code, page);
1264
- return { success: true, updatedContext: executionContext + action.newStep.code };
1265
- }
1266
- break;
1267
-
1268
- case StepOperation.REMOVE:
1269
- if (action.stepIndex !== undefined) {
1270
- // Remove step
1271
- steps.splice(action.stepIndex, 1);
1272
- return { success: true, updatedContext: executionContext };
1273
- }
1274
- break;
1275
- }
1276
-
1277
- return { success: false, error: 'Invalid repair action' };
1278
- } catch (error) {
1279
- return {
1280
- success: false,
1281
- error: error instanceof Error ? error.message : 'Unknown error during repair action'
1282
- };
1283
926
  }
1284
- }
1285
-
1286
- private async applyRepairAction(
1287
- action: StepRepairAction,
1288
- steps: (ScriptStep & { success?: boolean; error?: string })[],
1289
- currentIndex: number,
1290
- page: any
1291
- ): Promise<{ success: boolean; error?: string }> {
1292
- try {
1293
- switch (action.operation) {
1294
- case StepOperation.MODIFY:
1295
- if (action.newStep && action.stepIndex !== undefined) {
1296
- // Modify existing step
1297
- steps[action.stepIndex] = {
1298
- ...action.newStep,
1299
- success: false,
1300
- error: undefined
1301
- };
1302
- // Test the modified step
1303
- await this.executeStepCode(action.newStep.code, page);
1304
- return { success: true };
1305
- }
1306
- break;
1307
-
1308
- case StepOperation.INSERT:
1309
- if (action.newStep && action.insertAfterIndex !== undefined) {
1310
- // Insert new step after specified index
1311
- const insertIndex = action.insertAfterIndex + 1;
1312
- const newStep = {
1313
- ...action.newStep,
1314
- success: false,
1315
- error: undefined
1316
- };
1317
- steps.splice(insertIndex, 0, newStep);
1318
- // Test the inserted step
1319
- await this.executeStepCode(action.newStep.code, page);
1320
- return { success: true };
1321
- }
1322
- break;
1323
-
1324
- case StepOperation.REMOVE:
1325
- if (action.stepIndex !== undefined) {
1326
- // Remove the step
1327
- steps.splice(action.stepIndex, 1);
1328
- return { success: true };
1329
- }
1330
- break;
1331
- }
1332
-
1333
- return { success: false, error: 'Invalid repair action' };
1334
- } catch (error) {
1335
- return {
1336
- success: false,
1337
- error: error instanceof Error ? error.message : 'Repair action execution failed'
1338
- };
1339
- }
1340
- }
1341
-
1342
-
1343
-
1344
- private generateUpdatedScript(steps: (ScriptStep & { success?: boolean; error?: string })[], repairAdvice?: string): string {
927
+
1345
928
  const scriptLines = [
1346
929
  "import { test, expect } from '@playwright/test';",
1347
- `test('repairedTest', async ({ page, browser, context }) => {`
930
+ `test('${testName}', async ({ page, browser, context }) => {`
1348
931
  ];
1349
932
 
1350
933
  steps.forEach((step, index) => {
@@ -1361,8 +944,8 @@ Use these vision insights to inform your repair strategy.`;
1361
944
  scriptLines.push('});');
1362
945
  const script = scriptLines.join('\n');
1363
946
 
1364
- // Add TestChimp comment to the repaired script with repair advice
1365
- return addTestChimpComment(script, repairAdvice);
947
+ // Add TestChimp comment with hashtags and repair advice
948
+ return addTestChimpComment(script, repairAdvice, hashtags);
1366
949
  }
1367
950
 
1368
951