testchimp-runner-core 0.0.21 → 0.0.23

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 (146) hide show
  1. package/VISION_DIAGNOSTICS_IMPROVEMENTS.md +336 -0
  2. package/dist/credit-usage-service.d.ts +9 -0
  3. package/dist/credit-usage-service.d.ts.map +1 -1
  4. package/dist/credit-usage-service.js +20 -5
  5. package/dist/credit-usage-service.js.map +1 -1
  6. package/dist/execution-service.d.ts +7 -2
  7. package/dist/execution-service.d.ts.map +1 -1
  8. package/dist/execution-service.js +91 -36
  9. package/dist/execution-service.js.map +1 -1
  10. package/dist/index.d.ts +30 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +91 -26
  13. package/dist/index.js.map +1 -1
  14. package/dist/llm-facade.d.ts +64 -8
  15. package/dist/llm-facade.d.ts.map +1 -1
  16. package/dist/llm-facade.js +361 -109
  17. package/dist/llm-facade.js.map +1 -1
  18. package/dist/llm-provider.d.ts +39 -0
  19. package/dist/llm-provider.d.ts.map +1 -0
  20. package/dist/llm-provider.js +7 -0
  21. package/dist/llm-provider.js.map +1 -0
  22. package/dist/model-constants.d.ts +21 -0
  23. package/dist/model-constants.d.ts.map +1 -0
  24. package/dist/model-constants.js +24 -0
  25. package/dist/model-constants.js.map +1 -0
  26. package/dist/orchestrator/index.d.ts +8 -0
  27. package/dist/orchestrator/index.d.ts.map +1 -0
  28. package/dist/orchestrator/index.js +23 -0
  29. package/dist/orchestrator/index.js.map +1 -0
  30. package/dist/orchestrator/orchestrator-agent.d.ts +66 -0
  31. package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -0
  32. package/dist/orchestrator/orchestrator-agent.js +855 -0
  33. package/dist/orchestrator/orchestrator-agent.js.map +1 -0
  34. package/dist/orchestrator/tool-registry.d.ts +74 -0
  35. package/dist/orchestrator/tool-registry.d.ts.map +1 -0
  36. package/dist/orchestrator/tool-registry.js +131 -0
  37. package/dist/orchestrator/tool-registry.js.map +1 -0
  38. package/dist/orchestrator/tools/check-page-ready.d.ts +13 -0
  39. package/dist/orchestrator/tools/check-page-ready.d.ts.map +1 -0
  40. package/dist/orchestrator/tools/check-page-ready.js +72 -0
  41. package/dist/orchestrator/tools/check-page-ready.js.map +1 -0
  42. package/dist/orchestrator/tools/extract-data.d.ts +13 -0
  43. package/dist/orchestrator/tools/extract-data.d.ts.map +1 -0
  44. package/dist/orchestrator/tools/extract-data.js +84 -0
  45. package/dist/orchestrator/tools/extract-data.js.map +1 -0
  46. package/dist/orchestrator/tools/index.d.ts +10 -0
  47. package/dist/orchestrator/tools/index.d.ts.map +1 -0
  48. package/dist/orchestrator/tools/index.js +18 -0
  49. package/dist/orchestrator/tools/index.js.map +1 -0
  50. package/dist/orchestrator/tools/inspect-page.d.ts +13 -0
  51. package/dist/orchestrator/tools/inspect-page.d.ts.map +1 -0
  52. package/dist/orchestrator/tools/inspect-page.js +39 -0
  53. package/dist/orchestrator/tools/inspect-page.js.map +1 -0
  54. package/dist/orchestrator/tools/recall-history.d.ts +13 -0
  55. package/dist/orchestrator/tools/recall-history.d.ts.map +1 -0
  56. package/dist/orchestrator/tools/recall-history.js +64 -0
  57. package/dist/orchestrator/tools/recall-history.js.map +1 -0
  58. package/dist/orchestrator/tools/take-screenshot.d.ts +15 -0
  59. package/dist/orchestrator/tools/take-screenshot.d.ts.map +1 -0
  60. package/dist/orchestrator/tools/take-screenshot.js +112 -0
  61. package/dist/orchestrator/tools/take-screenshot.js.map +1 -0
  62. package/dist/orchestrator/types.d.ts +133 -0
  63. package/dist/orchestrator/types.d.ts.map +1 -0
  64. package/dist/orchestrator/types.js +28 -0
  65. package/dist/orchestrator/types.js.map +1 -0
  66. package/dist/playwright-mcp-service.d.ts +9 -0
  67. package/dist/playwright-mcp-service.d.ts.map +1 -1
  68. package/dist/playwright-mcp-service.js +20 -5
  69. package/dist/playwright-mcp-service.js.map +1 -1
  70. package/dist/progress-reporter.d.ts +97 -0
  71. package/dist/progress-reporter.d.ts.map +1 -0
  72. package/dist/progress-reporter.js +18 -0
  73. package/dist/progress-reporter.js.map +1 -0
  74. package/dist/prompts.d.ts +24 -0
  75. package/dist/prompts.d.ts.map +1 -1
  76. package/dist/prompts.js +593 -68
  77. package/dist/prompts.js.map +1 -1
  78. package/dist/providers/backend-proxy-llm-provider.d.ts +25 -0
  79. package/dist/providers/backend-proxy-llm-provider.d.ts.map +1 -0
  80. package/dist/providers/backend-proxy-llm-provider.js +76 -0
  81. package/dist/providers/backend-proxy-llm-provider.js.map +1 -0
  82. package/dist/providers/local-llm-provider.d.ts +21 -0
  83. package/dist/providers/local-llm-provider.d.ts.map +1 -0
  84. package/dist/providers/local-llm-provider.js +35 -0
  85. package/dist/providers/local-llm-provider.js.map +1 -0
  86. package/dist/scenario-service.d.ts +27 -1
  87. package/dist/scenario-service.d.ts.map +1 -1
  88. package/dist/scenario-service.js +48 -12
  89. package/dist/scenario-service.js.map +1 -1
  90. package/dist/scenario-worker-class.d.ts +39 -2
  91. package/dist/scenario-worker-class.d.ts.map +1 -1
  92. package/dist/scenario-worker-class.js +614 -86
  93. package/dist/scenario-worker-class.js.map +1 -1
  94. package/dist/script-utils.d.ts +2 -0
  95. package/dist/script-utils.d.ts.map +1 -1
  96. package/dist/script-utils.js +44 -4
  97. package/dist/script-utils.js.map +1 -1
  98. package/dist/types.d.ts +11 -0
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/types.js.map +1 -1
  101. package/dist/utils/browser-utils.d.ts +20 -1
  102. package/dist/utils/browser-utils.d.ts.map +1 -1
  103. package/dist/utils/browser-utils.js +102 -51
  104. package/dist/utils/browser-utils.js.map +1 -1
  105. package/dist/utils/page-info-utils.d.ts +23 -4
  106. package/dist/utils/page-info-utils.d.ts.map +1 -1
  107. package/dist/utils/page-info-utils.js +174 -43
  108. package/dist/utils/page-info-utils.js.map +1 -1
  109. package/package.json +1 -2
  110. package/plandocs/HUMAN_LIKE_IMPROVEMENTS.md +642 -0
  111. package/plandocs/MULTI_AGENT_ARCHITECTURE_REVIEW.md +844 -0
  112. package/plandocs/ORCHESTRATOR_MVP_SUMMARY.md +539 -0
  113. package/plandocs/PHASE1_ABSTRACTION_COMPLETE.md +241 -0
  114. package/plandocs/PHASE1_FINAL_STATUS.md +210 -0
  115. package/plandocs/PLANNING_SESSION_SUMMARY.md +372 -0
  116. package/plandocs/SCRIPT_CLEANUP_FEATURE.md +201 -0
  117. package/plandocs/SCRIPT_GENERATION_ARCHITECTURE.md +364 -0
  118. package/plandocs/SELECTOR_IMPROVEMENTS.md +139 -0
  119. package/src/credit-usage-service.ts +23 -5
  120. package/src/execution-service.ts +152 -42
  121. package/src/index.ts +169 -26
  122. package/src/llm-facade.ts +500 -126
  123. package/src/llm-provider.ts +43 -0
  124. package/src/model-constants.ts +23 -0
  125. package/src/orchestrator/index.ts +33 -0
  126. package/src/orchestrator/orchestrator-agent.ts +1037 -0
  127. package/src/orchestrator/tool-registry.ts +182 -0
  128. package/src/orchestrator/tools/check-page-ready.ts +75 -0
  129. package/src/orchestrator/tools/extract-data.ts +92 -0
  130. package/src/orchestrator/tools/index.ts +11 -0
  131. package/src/orchestrator/tools/inspect-page.ts +42 -0
  132. package/src/orchestrator/tools/recall-history.ts +72 -0
  133. package/src/orchestrator/tools/take-screenshot.ts +128 -0
  134. package/src/orchestrator/types.ts +200 -0
  135. package/src/playwright-mcp-service.ts +23 -5
  136. package/src/progress-reporter.ts +109 -0
  137. package/src/prompts.ts +606 -69
  138. package/src/providers/backend-proxy-llm-provider.ts +91 -0
  139. package/src/providers/local-llm-provider.ts +38 -0
  140. package/src/scenario-service.ts +83 -13
  141. package/src/scenario-worker-class.ts +740 -72
  142. package/src/script-utils.ts +50 -5
  143. package/src/types.ts +13 -1
  144. package/src/utils/browser-utils.ts +123 -51
  145. package/src/utils/page-info-utils.ts +210 -53
  146. package/testchimp-runner-core-0.0.22.tgz +0 -0
@@ -12,11 +12,15 @@ import {
12
12
  } from './types';
13
13
  import { RepairSuggestionResponse, RepairConfidenceResponse } from './llm-facade';
14
14
  import { getEnhancedPageInfo, PageInfo } from './utils/page-info-utils';
15
- import { initializeBrowser } from './utils/browser-utils';
15
+ import { initializeBrowser, captureOptimizedScreenshot } from './utils/browser-utils';
16
16
  import { LLMFacade } from './llm-facade';
17
17
  import { AuthConfig } from './auth-config';
18
18
  import { addTestChimpComment } from './script-utils';
19
19
  import { CreditUsageService } from './credit-usage-service';
20
+ import { DEFAULT_MODEL, VISION_MODEL } from './model-constants';
21
+ import { LLMProvider } from './llm-provider';
22
+ import { ProgressReporter } from './progress-reporter';
23
+ import { BackendProxyLLMProvider } from './providers/backend-proxy-llm-provider';
20
24
 
21
25
  /**
22
26
  * Service for orchestrating Playwright script execution
@@ -24,14 +28,27 @@ import { CreditUsageService } from './credit-usage-service';
24
28
  export class ExecutionService {
25
29
  private playwrightService: PlaywrightService;
26
30
  private llmFacade: LLMFacade;
31
+ private llmProvider: LLMProvider;
32
+ private progressReporter?: ProgressReporter;
27
33
  private creditUsageService: CreditUsageService;
28
34
  private maxConcurrentExecutions: number;
29
35
  private activeExecutions: Set<Promise<any>> = new Set();
30
36
  private logger?: (message: string, level?: 'log' | 'error' | 'warn') => void;
31
37
 
32
- constructor(authConfig?: AuthConfig, backendUrl?: string, maxConcurrentExecutions: number = 10) {
38
+ constructor(
39
+ authConfig?: AuthConfig,
40
+ backendUrl?: string,
41
+ maxConcurrentExecutions: number = 10,
42
+ llmProvider?: LLMProvider,
43
+ progressReporter?: ProgressReporter
44
+ ) {
33
45
  this.playwrightService = new PlaywrightService();
34
- this.llmFacade = new LLMFacade(authConfig, backendUrl);
46
+
47
+ // Use provided LLM provider or default to backend proxy (backward compatible)
48
+ this.llmProvider = llmProvider || new BackendProxyLLMProvider(authConfig, backendUrl);
49
+ this.llmFacade = new LLMFacade(this.llmProvider);
50
+
51
+ this.progressReporter = progressReporter;
35
52
  this.creditUsageService = new CreditUsageService(authConfig, backendUrl);
36
53
  this.maxConcurrentExecutions = maxConcurrentExecutions;
37
54
  }
@@ -44,20 +61,13 @@ export class ExecutionService {
44
61
  }
45
62
 
46
63
  /**
47
- * Log a message using the configured logger or console
64
+ * Log a message using the configured logger
48
65
  */
49
66
  private log(message: string, level: 'log' | 'error' | 'warn' = 'log'): void {
50
67
  if (this.logger) {
51
68
  this.logger(message, level);
52
- } else {
53
- if (level === 'error') {
54
- console.error(message);
55
- } else if (level === 'warn') {
56
- console.warn(message);
57
- } else {
58
- console.log(message);
59
- }
60
69
  }
70
+ // No console fallback - logs are routed to consumer
61
71
  }
62
72
 
63
73
  /**
@@ -69,9 +79,12 @@ export class ExecutionService {
69
79
 
70
80
  /**
71
81
  * Set authentication configuration for the service
82
+ * Note: This recreates the LLM provider with new auth config
72
83
  */
73
84
  setAuthConfig(authConfig: AuthConfig): void {
74
- this.llmFacade.setAuthConfig(authConfig);
85
+ // Recreate LLM provider with new auth config
86
+ this.llmProvider = new BackendProxyLLMProvider(authConfig, undefined);
87
+ this.llmFacade = new LLMFacade(this.llmProvider);
75
88
  this.creditUsageService.setAuthConfig(authConfig);
76
89
  }
77
90
 
@@ -102,7 +115,7 @@ export class ExecutionService {
102
115
  */
103
116
  private async executeScriptInternal(request: ScriptExecutionRequest): Promise<ScriptExecutionResponse> {
104
117
  const startTime = Date.now();
105
- const model = request.model || 'gpt-4.1-mini';
118
+ const model = request.model || DEFAULT_MODEL;
106
119
 
107
120
  try {
108
121
  if (request.mode === ExecutionMode.RUN_EXACTLY) {
@@ -327,7 +340,7 @@ export class ExecutionService {
327
340
 
328
341
  // Report credit usage for successful AI repair
329
342
  this.creditUsageService.reportAIRepairCredit().catch(error => {
330
- console.warn(`Failed to report credit usage for AI repair:`, error);
343
+ this.log(`Failed to report credit usage for AI repair: ${error}`, 'warn');
331
344
  });
332
345
 
333
346
  await repairBrowser.close();
@@ -488,6 +501,7 @@ export class ExecutionService {
488
501
  let repairSuccess = false;
489
502
  const originalDescription = step.description;
490
503
  const originalCode = step.code;
504
+ let usedVisionMode = false;
491
505
 
492
506
  for (let attempt = 1; attempt <= maxTries; attempt++) {
493
507
  this.log(`Step ${i + 1} repair attempt ${attempt}/${maxTries}`);
@@ -501,16 +515,109 @@ export class ExecutionService {
501
515
  // Build recent repairs context for LLM
502
516
  const recentRepairsContext = this.buildRecentRepairsContext(recentRepairs);
503
517
 
504
- // Ask AI for repair suggestion with failure history and recent repairs
505
- const repairSuggestion = await this.llmFacade.getRepairSuggestion(
506
- step.description,
507
- step.code,
508
- step.error || 'Unknown error',
509
- pageInfo,
510
- failureHistory,
511
- recentRepairsContext,
512
- model
513
- );
518
+ let repairSuggestion;
519
+
520
+ // VISION-BASED FALLBACK: After 2 regular repair attempts, consider vision diagnostics on final attempt
521
+ if (attempt === maxTries - 1 && repairHistory.length >= 2 && !usedVisionMode) {
522
+ // Ask LLM if screenshot would help for repair diagnostics
523
+ this.log(` 🤔 After ${repairHistory.length} failed repairs: Asking LLM if screenshot would help (last resort)...`);
524
+
525
+ const screenshotNeed = await this.llmFacade.assessScreenshotNeed(
526
+ step.description,
527
+ step.error || 'Unknown error',
528
+ repairHistory.length + 1,
529
+ pageInfo,
530
+ model
531
+ );
532
+
533
+ this.log(` 💭 LLM assessment: ${screenshotNeed.needsScreenshot ? 'SCREENSHOT NEEDED' : 'NO SCREENSHOT'} - ${screenshotNeed.reason}`);
534
+
535
+ if (screenshotNeed.needsScreenshot) {
536
+ // Two-step supervisor pattern for vision-based repair:
537
+ // 1. Supervisor analyzes screenshot and provides diagnostic insights
538
+ // 2. Get repair suggestion with enhanced context from vision analysis
539
+
540
+ this.log(` 📸 Taking screenshot for supervisor analysis...`);
541
+
542
+ // Capture optimized screenshot using utility method
543
+ const imageDataUrl = await captureOptimizedScreenshot(
544
+ page,
545
+ { timeout: 10000 }, // Uses default quality 60
546
+ (msg) => this.log(msg)
547
+ );
548
+
549
+ this.log(` 👔 STEP 1: Supervisor analyzing screenshot (${VISION_MODEL})...`);
550
+ const supervisorDiagnostics = await this.llmFacade.getVisionDiagnostics(
551
+ step.description,
552
+ pageInfo,
553
+ [], // No previous steps context for repair
554
+ step.error,
555
+ imageDataUrl,
556
+ VISION_MODEL
557
+ );
558
+
559
+ // DEBUG: Log vision diagnostics
560
+ this.log(` 📸 Visual insights: ${supervisorDiagnostics.visualAnalysis}`);
561
+ this.log(` 🔍 Root cause: ${supervisorDiagnostics.rootCause}`);
562
+ this.log(` 💡 Recommended approach: ${supervisorDiagnostics.recommendedApproach}`);
563
+ if (supervisorDiagnostics.elementsFound.length > 0) {
564
+ this.log(` ✅ Elements found: ${supervisorDiagnostics.elementsFound.join(', ')}`);
565
+ }
566
+ if (supervisorDiagnostics.elementsNotFound.length > 0) {
567
+ this.log(` ❌ Elements not found: ${supervisorDiagnostics.elementsNotFound.join(', ')}`);
568
+ }
569
+
570
+ // Get repair suggestion with vision-enhanced context
571
+ this.log(` 🔨 STEP 2: Getting repair suggestion with vision insights...`);
572
+ const visionEnhancedFailureHistory = `${failureHistory}
573
+
574
+ VISION-BASED DIAGNOSTIC INSIGHTS:
575
+ Visual Analysis: ${supervisorDiagnostics.visualAnalysis}
576
+ Root Cause: ${supervisorDiagnostics.rootCause}
577
+ Recommended Approach: ${supervisorDiagnostics.recommendedApproach}
578
+ Elements Found: ${supervisorDiagnostics.elementsFound.join(', ') || 'None'}
579
+ Elements Not Found: ${supervisorDiagnostics.elementsNotFound.join(', ') || 'None'}
580
+
581
+ Use these vision insights to inform your repair strategy.`;
582
+
583
+ repairSuggestion = await this.llmFacade.getRepairSuggestion(
584
+ step.description,
585
+ step.code,
586
+ step.error || 'Unknown error',
587
+ pageInfo,
588
+ visionEnhancedFailureHistory,
589
+ recentRepairsContext,
590
+ model
591
+ );
592
+
593
+ usedVisionMode = true;
594
+ } else {
595
+ // Regular repair without vision
596
+ if (screenshotNeed.alternativeApproach) {
597
+ this.log(` 💡 Alternative approach: ${screenshotNeed.alternativeApproach}`);
598
+ }
599
+ repairSuggestion = await this.llmFacade.getRepairSuggestion(
600
+ step.description,
601
+ step.code,
602
+ step.error || 'Unknown error',
603
+ pageInfo,
604
+ failureHistory,
605
+ recentRepairsContext,
606
+ model
607
+ );
608
+ }
609
+ } else {
610
+ // Regular repair attempt (first 2 attempts or already used vision)
611
+ repairSuggestion = await this.llmFacade.getRepairSuggestion(
612
+ step.description,
613
+ step.code,
614
+ step.error || 'Unknown error',
615
+ pageInfo,
616
+ failureHistory,
617
+ recentRepairsContext,
618
+ model
619
+ );
620
+ }
514
621
 
515
622
  if (!repairSuggestion.shouldContinue) {
516
623
  this.log(`AI decided to stop repair at attempt ${attempt}: ${repairSuggestion.reason}`);
@@ -562,7 +669,7 @@ export class ExecutionService {
562
669
  repairAction.operation === StepOperation.REMOVE ?
563
670
  `REMOVE: step at index ${repairAction.stepIndex}` :
564
671
  repairAction.operation;
565
- this.log(`Step ${i + 1} repair action ${commandInfo} executed successfully on attempt ${attempt}`);
672
+ this.log(`Step ${i + 1} repair action ${commandInfo} executed successfully on attempt ${attempt}${usedVisionMode ? ' (vision-aided)' : ''}`);
566
673
 
567
674
  // Update execution context based on the repair action
568
675
  if (repairAction.operation === StepOperation.MODIFY && repairAction.newStep) {
@@ -661,8 +768,9 @@ export class ExecutionService {
661
768
  }
662
769
 
663
770
  private async executeStepCode(code: string, page: any): Promise<void> {
664
- // Set timeout for individual step execution
665
- page.setDefaultTimeout(5000); // 5 seconds for individual commands
771
+ // Keep default timeout (5 seconds) for fast feedback on wrong selectors
772
+ // Navigation operations should use explicit longer timeouts in generated code
773
+ page.setDefaultTimeout(5000);
666
774
 
667
775
  try {
668
776
  // Clean and validate the code before execution
@@ -680,8 +788,8 @@ export class ExecutionService {
680
788
  const result = executeCode(page, expect);
681
789
  await result;
682
790
  } finally {
683
- // Restore to reasonable default timeout
684
- page.setDefaultTimeout(10000);
791
+ // Ensure timeout remains consistent
792
+ page.setDefaultTimeout(5000);
685
793
  }
686
794
  }
687
795
 
@@ -709,8 +817,8 @@ export class ExecutionService {
709
817
  executionContext: string,
710
818
  contextVariables: Map<string, any>
711
819
  ): Promise<void> {
712
- // Set timeout for individual step execution
713
- page.setDefaultTimeout(5000); // 5 seconds for individual commands
820
+ // Keep default timeout (5 seconds) for fast feedback
821
+ page.setDefaultTimeout(5000);
714
822
 
715
823
  try {
716
824
  // Execute only the current step code, but make context variables available
@@ -779,10 +887,9 @@ export class ExecutionService {
779
887
  return {
780
888
  url: page.url(),
781
889
  title: 'Unknown',
782
- elements: 'Unable to extract',
783
- formFields: 'Unable to extract',
784
- interactiveElements: 'Unable to extract',
785
- pageStructure: 'Unable to extract'
890
+ ariaSnapshot: null,
891
+ interactiveElements: [],
892
+ formattedElements: 'Unable to extract'
786
893
  };
787
894
  }
788
895
  }
@@ -1004,11 +1111,14 @@ export class ExecutionService {
1004
1111
  ];
1005
1112
 
1006
1113
  steps.forEach((step, index) => {
1007
- scriptLines.push(` // Step ${index + 1}: ${step.description}`);
1008
- const codeLines = step.code.split('\n');
1009
- codeLines.forEach(line => {
1010
- scriptLines.push(` ${line}`);
1011
- });
1114
+ // Only add step if it has code to execute
1115
+ if (step.code && step.code.trim().length > 0) {
1116
+ scriptLines.push(` // ${step.description}`);
1117
+ const codeLines = step.code.split('\n');
1118
+ codeLines.forEach(line => {
1119
+ scriptLines.push(` ${line}`);
1120
+ });
1121
+ }
1012
1122
  });
1013
1123
 
1014
1124
  scriptLines.push('});');
@@ -1023,7 +1133,7 @@ export class ExecutionService {
1023
1133
  * Initialize browser with configuration (delegates to utility function)
1024
1134
  */
1025
1135
  private async initializeBrowser(playwrightConfig?: string, headless?: boolean, playwrightConfigFilePath?: string): Promise<{ browser: any; context: any; page: any }> {
1026
- return initializeBrowser(playwrightConfig, headless, playwrightConfigFilePath);
1136
+ return initializeBrowser(playwrightConfig, headless, playwrightConfigFilePath, this.logger);
1027
1137
  }
1028
1138
 
1029
1139
  /**
package/src/index.ts CHANGED
@@ -18,8 +18,45 @@ export { ExecutionService, ScenarioService, ScenarioWorker, PlaywrightMCPService
18
18
  import { FileHandler, LocalFileHandler, CIFileHandler, NoOpFileHandler } from './file-handler';
19
19
  export { FileHandler, LocalFileHandler, CIFileHandler, NoOpFileHandler };
20
20
 
21
+ // LLM Provider interfaces
22
+ import { LLMProvider, LLMRequest, LLMResponse } from './llm-provider';
23
+ import { ProgressReporter, StepProgress, JobProgress, StepExecutionStatus } from './progress-reporter';
24
+ import { BackendProxyLLMProvider } from './providers/backend-proxy-llm-provider';
25
+ import { LocalLLMProvider } from './providers/local-llm-provider';
26
+
27
+ export { LLMProvider, LLMRequest, LLMResponse };
28
+ export { ProgressReporter, StepProgress, JobProgress, StepExecutionStatus };
29
+ export { BackendProxyLLMProvider, LocalLLMProvider };
30
+
31
+ // Orchestrator (tool-using agent)
32
+ import type { AgentConfig } from './orchestrator';
33
+ export {
34
+ OrchestratorAgent,
35
+ ToolRegistry,
36
+ Tool,
37
+ ToolParameter,
38
+ ToolExecutionContext,
39
+ AgentConfig,
40
+ AgentContext,
41
+ AgentDecision,
42
+ JourneyMemory,
43
+ MemoryStep,
44
+ OrchestratorStepResult,
45
+ SelfReflection,
46
+ ToolCall,
47
+ ToolResult,
48
+ DEFAULT_AGENT_CONFIG,
49
+ // Tools (information-gathering only)
50
+ TakeScreenshotTool,
51
+ RecallHistoryTool,
52
+ InspectPageTool,
53
+ CheckPageReadyTool,
54
+ ExtractDataTool
55
+ } from './orchestrator';
56
+
21
57
  // Types
22
58
  export * from './types';
59
+ export { PageInfo, InteractiveElement } from './utils/page-info-utils';
23
60
 
24
61
  // Authentication
25
62
  export * from './auth-config';
@@ -35,39 +72,96 @@ export class TestChimpService {
35
72
  private executionService: ExecutionService;
36
73
  public scenarioService: ScenarioService; // Make public for event listening
37
74
  private playwrightService: PlaywrightMCPService;
38
- private llmFacade: LLMFacade;
75
+ private llmProvider: LLMProvider;
76
+ private progressReporter?: ProgressReporter;
39
77
  private creditUsageService: CreditUsageService;
40
78
  private fileHandler: FileHandler;
41
79
  private authConfig: AuthConfig | null;
42
80
  private backendUrl: string;
81
+ private logger?: (message: string, level?: 'log' | 'error' | 'warn') => void;
82
+ private orchestratorOptions?: { useOrchestrator?: boolean; orchestratorConfig?: Partial<AgentConfig>; debugMode?: boolean };
43
83
 
44
- constructor(fileHandler?: FileHandler, authConfig?: AuthConfig, backendUrl?: string, maxWorkers?: number) {
84
+ constructor(
85
+ fileHandler?: FileHandler,
86
+ authConfig?: AuthConfig,
87
+ backendUrl?: string,
88
+ maxWorkers?: number,
89
+ llmProvider?: LLMProvider,
90
+ progressReporter?: ProgressReporter,
91
+ orchestratorOptions?: { useOrchestrator?: boolean; orchestratorConfig?: Partial<AgentConfig>; debugMode?: boolean }
92
+ ) {
45
93
  this.fileHandler = fileHandler || new NoOpFileHandler();
46
94
  this.authConfig = authConfig || null;
47
95
  this.backendUrl = backendUrl || 'https://featureservice.testchimp.io'; // Default to production
96
+ this.progressReporter = progressReporter;
97
+ this.orchestratorOptions = orchestratorOptions;
98
+
99
+ // Use provided LLM provider or default to backend proxy (backward compatible)
100
+ this.llmProvider = llmProvider || new BackendProxyLLMProvider(authConfig, backendUrl);
101
+
48
102
  this.playwrightService = new PlaywrightMCPService();
49
- this.llmFacade = new LLMFacade(this.authConfig || undefined, this.backendUrl);
50
103
  this.creditUsageService = new CreditUsageService(this.authConfig || undefined, this.backendUrl);
51
- this.executionService = new ExecutionService(this.authConfig || undefined, this.backendUrl, maxWorkers || 10);
52
- // Set the credit usage service for the execution service
53
- this.executionService.setAuthConfig(this.authConfig || {} as AuthConfig);
54
- this.scenarioService = new ScenarioService(maxWorkers || 2, this.fileHandler, this.authConfig || undefined, this.backendUrl);
104
+
105
+ // Create services with providers
106
+ this.executionService = new ExecutionService(
107
+ this.authConfig || undefined,
108
+ this.backendUrl,
109
+ maxWorkers || 10,
110
+ this.llmProvider, // Pass the LLM provider
111
+ this.progressReporter // Pass the progress reporter
112
+ );
113
+
114
+ this.scenarioService = new ScenarioService(
115
+ maxWorkers || 2,
116
+ this.fileHandler,
117
+ this.llmProvider,
118
+ this.progressReporter,
119
+ this.authConfig || undefined,
120
+ this.backendUrl,
121
+ this.orchestratorOptions // Pass orchestrator options
122
+ );
55
123
  }
56
124
 
57
125
  /**
58
126
  * Set authentication configuration for the service
127
+ * Recreates LLM provider and services with new auth config
59
128
  */
60
129
  async setAuthConfig(authConfig: AuthConfig): Promise<void> {
61
130
  this.authConfig = authConfig;
62
- this.llmFacade.setAuthConfig(authConfig);
63
131
  this.creditUsageService.setAuthConfig(authConfig);
64
132
 
65
- // Recreate services with new auth config to ensure all workers get the updated config
66
- this.executionService = new ExecutionService(this.authConfig, this.backendUrl, 10);
67
- this.scenarioService = new ScenarioService(2, this.fileHandler, this.authConfig, this.backendUrl);
133
+ // Recreate LLM provider with new auth config
134
+ this.llmProvider = new BackendProxyLLMProvider(authConfig, this.backendUrl);
68
135
 
69
- // Set auth config for the execution service
70
- this.executionService.setAuthConfig(authConfig);
136
+ // Set logger on new provider if we have one
137
+ if (this.logger) {
138
+ this.llmProvider.setLogger?.(this.logger);
139
+ }
140
+
141
+ // Recreate services with new provider AND pass llmProvider and progressReporter
142
+ this.executionService = new ExecutionService(
143
+ this.authConfig,
144
+ this.backendUrl,
145
+ 10,
146
+ this.llmProvider, // Pass the LLM provider
147
+ this.progressReporter // Pass the progress reporter
148
+ );
149
+
150
+ this.scenarioService = new ScenarioService(
151
+ 2,
152
+ this.fileHandler,
153
+ this.llmProvider,
154
+ this.progressReporter,
155
+ this.authConfig,
156
+ this.backendUrl,
157
+ this.orchestratorOptions // Pass orchestrator options
158
+ );
159
+
160
+ // Set logger on recreated services if we have one
161
+ if (this.logger) {
162
+ this.executionService.setLogger(this.logger);
163
+ this.scenarioService.setLogger(this.logger);
164
+ }
71
165
 
72
166
  // Reinitialize the services
73
167
  await this.executionService.initialize();
@@ -76,18 +170,43 @@ export class TestChimpService {
76
170
 
77
171
  /**
78
172
  * Set backend URL for the service
173
+ * Recreates LLM provider and services with new backend URL
79
174
  */
80
175
  setBackendUrl(backendUrl: string): void {
81
176
  this.backendUrl = backendUrl;
82
- // Recreate services with new backend URL
83
- this.llmFacade = new LLMFacade(this.authConfig || undefined, this.backendUrl);
177
+
178
+ // Recreate LLM provider with new backend URL
179
+ this.llmProvider = new BackendProxyLLMProvider(this.authConfig || undefined, backendUrl);
180
+
181
+ // Set logger on new provider if we have one
182
+ if (this.logger) {
183
+ this.llmProvider.setLogger?.(this.logger);
184
+ }
185
+
186
+ // Recreate services with new provider
84
187
  this.creditUsageService = new CreditUsageService(this.authConfig || undefined, this.backendUrl);
85
- this.executionService = new ExecutionService(this.authConfig || undefined, this.backendUrl, 10);
86
- this.scenarioService = new ScenarioService(2, this.fileHandler, this.authConfig || undefined, this.backendUrl);
188
+ this.executionService = new ExecutionService(
189
+ this.authConfig || undefined,
190
+ this.backendUrl,
191
+ 10,
192
+ this.llmProvider, // Pass the LLM provider
193
+ this.progressReporter // Pass the progress reporter
194
+ );
195
+
196
+ this.scenarioService = new ScenarioService(
197
+ 2,
198
+ this.fileHandler,
199
+ this.llmProvider,
200
+ this.progressReporter,
201
+ this.authConfig || undefined,
202
+ this.backendUrl,
203
+ this.orchestratorOptions // Pass orchestrator options
204
+ );
87
205
 
88
- // Set auth config for the execution service
89
- if (this.authConfig) {
90
- this.executionService.setAuthConfig(this.authConfig);
206
+ // Set logger on recreated services if we have one
207
+ if (this.logger) {
208
+ this.executionService.setLogger(this.logger);
209
+ this.scenarioService.setLogger(this.logger);
91
210
  }
92
211
  }
93
212
 
@@ -95,7 +214,31 @@ export class TestChimpService {
95
214
  * Set logger callback for capturing execution logs
96
215
  */
97
216
  setLogger(logger: (message: string, level?: 'log' | 'error' | 'warn') => void): void {
217
+ this.logger = logger;
98
218
  this.executionService.setLogger(logger);
219
+ this.scenarioService.setLogger(logger);
220
+ this.llmProvider.setLogger?.(logger);
221
+ this.creditUsageService.setLogger(logger);
222
+ }
223
+
224
+ /**
225
+ * Set output channel for worker logs (VS Code OutputChannel)
226
+ * This enables orchestrator thinking logs to appear in output console
227
+ */
228
+ setOutputChannel(outputChannel: any): void {
229
+ if (typeof this.scenarioService?.setOutputChannel === 'function') {
230
+ this.scenarioService.setOutputChannel(outputChannel);
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Log a message using the configured logger
236
+ */
237
+ private log(message: string, level: 'log' | 'error' | 'warn' = 'log'): void {
238
+ if (this.logger) {
239
+ this.logger(message, level);
240
+ }
241
+ // No console fallback - logs are routed to consumer
99
242
  }
100
243
 
101
244
  /**
@@ -127,7 +270,7 @@ export class TestChimpService {
127
270
  try {
128
271
  const resolvedPath = this.fileHandler.resolvePath(request.scriptFilePath);
129
272
  request.script = await this.fileHandler.readTestFile(resolvedPath);
130
- console.log(`Read script content from file: ${resolvedPath}`);
273
+ this.log(`Read script content from file: ${resolvedPath}`);
131
274
  } catch (error) {
132
275
  throw new Error(`Failed to read script file: ${error}`);
133
276
  }
@@ -138,24 +281,24 @@ export class TestChimpService {
138
281
  try {
139
282
  const resolvedPath = this.fileHandler.resolvePath(request.playwrightConfigFilePath);
140
283
  request.playwrightConfig = await this.fileHandler.readTestFile(resolvedPath);
141
- console.log(`Read Playwright config content from file: ${resolvedPath}`);
284
+ this.log(`Read Playwright config content from file: ${resolvedPath}`);
142
285
  } catch (error) {
143
- console.warn(`Failed to read Playwright config file: ${error}. Using default configuration.`);
286
+ this.log(`Failed to read Playwright config file: ${error}. Using default configuration.`, 'warn');
144
287
  // Don't throw error, just use default config
145
288
  }
146
289
  }
147
290
 
148
291
  // Log content status
149
292
  if (request.script) {
150
- console.log(`Using provided script content (${request.script.length} characters)`);
293
+ this.log(`Using provided script content (${request.script.length} characters)`);
151
294
  } else {
152
295
  throw new Error('Script content is required. Provide either script or scriptFilePath.');
153
296
  }
154
297
 
155
298
  if (request.playwrightConfig) {
156
- console.log(`Using provided Playwright config (${request.playwrightConfig.length} characters)`);
299
+ this.log(`Using provided Playwright config (${request.playwrightConfig.length} characters)`);
157
300
  } else {
158
- console.log(`Using default Playwright configuration`);
301
+ this.log(`Using default Playwright configuration`);
159
302
  }
160
303
 
161
304
  const result = await this.executionService.executeScript(request);