testchimp-runner-core 0.0.39 → 0.0.41

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 (77) hide show
  1. package/dist/execution-service.d.ts.map +1 -1
  2. package/dist/execution-service.js +1 -3
  3. package/dist/execution-service.js.map +1 -1
  4. package/dist/index.d.ts +7 -6
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -4
  7. package/dist/index.js.map +1 -1
  8. package/dist/orchestrator/decision-parser.d.ts.map +1 -1
  9. package/dist/orchestrator/decision-parser.js +16 -0
  10. package/dist/orchestrator/decision-parser.js.map +1 -1
  11. package/dist/orchestrator/index.d.ts +3 -1
  12. package/dist/orchestrator/index.d.ts.map +1 -1
  13. package/dist/orchestrator/index.js +8 -1
  14. package/dist/orchestrator/index.js.map +1 -1
  15. package/dist/orchestrator/orchestrator-agent.d.ts +10 -4
  16. package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -1
  17. package/dist/orchestrator/orchestrator-agent.js +347 -93
  18. package/dist/orchestrator/orchestrator-agent.js.map +1 -1
  19. package/dist/orchestrator/orchestrator-prompts.d.ts.map +1 -1
  20. package/dist/orchestrator/orchestrator-prompts.js +364 -415
  21. package/dist/orchestrator/orchestrator-prompts.js.map +1 -1
  22. package/dist/orchestrator/page-loading-utils.d.ts +15 -0
  23. package/dist/orchestrator/page-loading-utils.d.ts.map +1 -0
  24. package/dist/orchestrator/page-loading-utils.js +115 -0
  25. package/dist/orchestrator/page-loading-utils.js.map +1 -0
  26. package/dist/orchestrator/page-som-handler.d.ts +2 -1
  27. package/dist/orchestrator/page-som-handler.d.ts.map +1 -1
  28. package/dist/orchestrator/page-som-handler.js +250 -33
  29. package/dist/orchestrator/page-som-handler.js.map +1 -1
  30. package/dist/orchestrator/site-learnings-utils.d.ts +31 -0
  31. package/dist/orchestrator/site-learnings-utils.d.ts.map +1 -0
  32. package/dist/orchestrator/site-learnings-utils.js +175 -0
  33. package/dist/orchestrator/site-learnings-utils.js.map +1 -0
  34. package/dist/orchestrator/som-types.d.ts +2 -0
  35. package/dist/orchestrator/som-types.d.ts.map +1 -1
  36. package/dist/orchestrator/som-types.js.map +1 -1
  37. package/dist/orchestrator/tools/take-screenshot.d.ts.map +1 -1
  38. package/dist/orchestrator/tools/take-screenshot.js +10 -1
  39. package/dist/orchestrator/tools/take-screenshot.js.map +1 -1
  40. package/dist/orchestrator/types.d.ts +54 -9
  41. package/dist/orchestrator/types.d.ts.map +1 -1
  42. package/dist/orchestrator/types.js.map +1 -1
  43. package/dist/progress-reporter.d.ts +23 -2
  44. package/dist/progress-reporter.d.ts.map +1 -1
  45. package/dist/progress-reporter.js.map +1 -1
  46. package/dist/scenario-service.d.ts +3 -3
  47. package/dist/scenario-service.d.ts.map +1 -1
  48. package/dist/scenario-service.js +6 -5
  49. package/dist/scenario-service.js.map +1 -1
  50. package/dist/scenario-worker-class.d.ts +7 -3
  51. package/dist/scenario-worker-class.d.ts.map +1 -1
  52. package/dist/scenario-worker-class.js +62 -9
  53. package/dist/scenario-worker-class.js.map +1 -1
  54. package/dist/types.d.ts +4 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/types.js.map +1 -1
  57. package/package.json +1 -1
  58. package/dist/testing/agent-tester.d.ts +0 -35
  59. package/dist/testing/agent-tester.d.ts.map +0 -1
  60. package/dist/testing/agent-tester.js +0 -84
  61. package/dist/testing/agent-tester.js.map +0 -1
  62. package/dist/testing/ref-translator-tester.d.ts +0 -44
  63. package/dist/testing/ref-translator-tester.d.ts.map +0 -1
  64. package/dist/testing/ref-translator-tester.js +0 -104
  65. package/dist/testing/ref-translator-tester.js.map +0 -1
  66. package/dist/utils/hierarchical-selector.d.ts +0 -47
  67. package/dist/utils/hierarchical-selector.d.ts.map +0 -1
  68. package/dist/utils/hierarchical-selector.js +0 -212
  69. package/dist/utils/hierarchical-selector.js.map +0 -1
  70. package/dist/utils/ref-attacher.d.ts +0 -21
  71. package/dist/utils/ref-attacher.d.ts.map +0 -1
  72. package/dist/utils/ref-attacher.js +0 -149
  73. package/dist/utils/ref-attacher.js.map +0 -1
  74. package/dist/utils/ref-translator.d.ts +0 -49
  75. package/dist/utils/ref-translator.d.ts.map +0 -1
  76. package/dist/utils/ref-translator.js +0 -276
  77. package/dist/utils/ref-translator.js.map +0 -1
@@ -9,6 +9,10 @@ const progress_reporter_1 = require("../progress-reporter");
9
9
  const page_info_utils_1 = require("../utils/page-info-utils");
10
10
  const coordinate_converter_1 = require("../utils/coordinate-converter");
11
11
  const model_constants_1 = require("../model-constants");
12
+ // @ts-ignore - package.json exists
13
+ const package_json_1 = require("../../package.json");
14
+ const site_learnings_utils_1 = require("./site-learnings-utils");
15
+ const page_loading_utils_1 = require("./page-loading-utils");
12
16
  const types_1 = require("./types");
13
17
  const orchestrator_prompts_1 = require("./orchestrator-prompts");
14
18
  const page_info_retry_1 = require("../utils/page-info-retry");
@@ -21,6 +25,14 @@ const som_types_1 = require("./som-types");
21
25
  class OrchestratorAgent {
22
26
  constructor(llmFacade, toolRegistry, config, progressReporter, logger, debugMode) {
23
27
  this.debugMode = false;
28
+ // Debug stats tracking
29
+ this.debugStats = {
30
+ tokensUsedIn: 0,
31
+ tokensUsedOut: 0,
32
+ imagesUsed: 0,
33
+ toolsUsed: {},
34
+ promptImproveSuggestions: []
35
+ };
24
36
  this.llmFacade = llmFacade;
25
37
  this.toolRegistry = toolRegistry;
26
38
  this.config = { ...types_1.DEFAULT_AGENT_CONFIG, ...config };
@@ -43,10 +55,19 @@ class OrchestratorAgent {
43
55
  nextSteps, // For repair mode: steps after this one
44
56
  successfulCommandsInStep, // For repair mode: commands that succeeded within THIS step
45
57
  failingCommand, // For repair mode: the specific command that failed
46
- remainingCommandsInStep // For repair mode: commands after the failing one
58
+ remainingCommandsInStep, // For repair mode: commands after the failing one
59
+ existingSiteLearnings // Pre-existing site learnings from previous runs
47
60
  ) {
48
61
  this.logger?.(`\n[Orchestrator] ========== STEP ${stepNumber}/${totalSteps} ==========`);
62
+ this.logger?.(`[Orchestrator] 🚀 runner-core v${package_json_1.version}`);
49
63
  this.logger?.(`[Orchestrator] 🎯 Goal: ${stepDescription}`);
64
+ // Site learnings (persistent across journeys) - initialize with existing or empty
65
+ const siteLearnings = existingSiteLearnings || { screens: {}, uxPatterns: {} };
66
+ if (existingSiteLearnings) {
67
+ const screenCount = Object.keys(existingSiteLearnings.screens).length;
68
+ const patternCount = Object.keys(existingSiteLearnings.uxPatterns).length;
69
+ this.logger?.(`[Orchestrator] 📚 Loaded existing learnings: ${screenCount} screens, ${patternCount} UX patterns`);
70
+ }
50
71
  let iteration = 0;
51
72
  let noteToSelf = memory.latestNote; // Start with note from previous step
52
73
  const commandsExecuted = [];
@@ -56,8 +77,8 @@ class OrchestratorAgent {
56
77
  iteration++;
57
78
  this.logger?.(`\n[Orchestrator] === Iteration ${iteration}/${this.config.maxIterationsPerStep} ===`);
58
79
  // Build context for agent
59
- const context = await this.buildAgentContext(page, stepDescription, stepNumber, totalSteps, scenarioSteps, memory, consecutiveFailures, noteToSelf, // Pass note from previous iteration
60
- priorSteps, // Repair context: prior completed steps
80
+ const context = await this.buildAgentContext(page, stepDescription, stepNumber, totalSteps, scenarioSteps, memory, siteLearnings, // Site learnings (persistent)
81
+ consecutiveFailures, priorSteps, // Repair context: prior completed steps
61
82
  nextSteps, // Repair context: next steps
62
83
  successfulCommandsInStep, // Repair context: successful commands in THIS step
63
84
  failingCommand, // Repair context: the failing command
@@ -67,6 +88,30 @@ class OrchestratorAgent {
67
88
  const decision = await this.callAgent(context, jobId, stepNumber, iteration, consecutiveFailures);
68
89
  // Log agent's reasoning
69
90
  this.decisionParser.log(decision, iteration);
91
+ // Handle debug info from agent
92
+ if (decision.debugInfo) {
93
+ // Collect prompt improvement suggestions
94
+ if (decision.debugInfo.suggestedPromptUpdates) {
95
+ this.debugStats.promptImproveSuggestions.push(decision.debugInfo.suggestedPromptUpdates);
96
+ this.logger?.(`[Orchestrator] 💡 Prompt suggestion collected: ${decision.debugInfo.suggestedPromptUpdates.substring(0, 80)}...`, 'log');
97
+ }
98
+ // Process tool usefulness feedback (for tools from PREVIOUS iteration)
99
+ if (decision.debugInfo.toolUsefulnessFeedback) {
100
+ for (const [toolName, rating] of Object.entries(decision.debugInfo.toolUsefulnessFeedback)) {
101
+ if (this.debugStats.toolsUsed[toolName]) {
102
+ const stats = this.debugStats.toolsUsed[toolName];
103
+ // Calculate running average: (oldAvg * oldCount + newRating) / newCount
104
+ const oldTotal = stats.averageUsefulnessScore * stats.numTimesRated;
105
+ stats.numTimesRated++;
106
+ stats.averageUsefulnessScore = (oldTotal + rating) / stats.numTimesRated;
107
+ this.logger?.(`[Orchestrator] ⭐ Tool feedback: ${toolName} rated ${rating}/5 (avg: ${stats.averageUsefulnessScore.toFixed(2)})`, 'log');
108
+ }
109
+ else {
110
+ this.logger?.(`[Orchestrator] ⚠️ Tool feedback for unknown tool: ${toolName}`, 'warn');
111
+ }
112
+ }
113
+ }
114
+ }
70
115
  // Report progress
71
116
  await this.reportStepProgress(jobId, stepNumber, stepDescription, decision, iteration);
72
117
  // Execute tools if requested (tools are READ-ONLY, they don't change state)
@@ -82,6 +127,7 @@ class OrchestratorAgent {
82
127
  this.logger?.(`[Orchestrator] ⚠️ WARNING: ${recentScreenshots.length} screenshots in last 3 iterations - agent may be looping`, 'warn');
83
128
  }
84
129
  if (decision.toolCalls && decision.toolCalls.length > 0) {
130
+ this.logger?.(`[Orchestrator] 🔧 Agent using TOOL CALLS: ${decision.toolCalls.map(tc => tc.name).join(', ')}`);
85
131
  // ENFORCE: Block screenshot tool calls if too many taken IN THIS STEP
86
132
  if (screenshotsThisStep.length >= 3) {
87
133
  decision.toolCalls = decision.toolCalls.filter(tc => tc.name !== 'take_screenshot');
@@ -96,27 +142,28 @@ class OrchestratorAgent {
96
142
  }
97
143
  }
98
144
  if (decision.toolCalls.length > 0) {
99
- toolResults = await this.executeTools(decision.toolCalls, page, memory, stepNumber, context.currentPageInfo.refMap);
100
- }
101
- // If agent wants to wait for tool results before proceeding, call agent again
102
- if (decision.needsToolResults) {
103
- const updatedContext = { ...context, toolResults };
104
- const continuedDecision = await this.callAgent(updatedContext, jobId, stepNumber, iteration, consecutiveFailures);
105
- // Merge continued decision
106
- decision.commands = continuedDecision.commands || decision.commands;
107
- decision.commandReasoning = continuedDecision.commandReasoning || decision.commandReasoning;
108
- decision.status = continuedDecision.status;
109
- decision.statusReasoning = continuedDecision.statusReasoning;
110
- decision.reasoning = continuedDecision.reasoning;
145
+ toolResults = await this.executeTools(decision.toolCalls, page, memory, stepNumber);
111
146
  }
112
147
  }
148
+ // If agent wants to wait for tool results before proceeding, call agent again
149
+ if (decision.toolCalls && decision.toolCalls.length > 0 && decision.needsToolResults) {
150
+ const updatedContext = { ...context, toolResults };
151
+ const continuedDecision = await this.callAgent(updatedContext, jobId, stepNumber, iteration, consecutiveFailures);
152
+ // Merge continued decision
153
+ decision.commands = continuedDecision.commands || decision.commands;
154
+ decision.commandReasoning = continuedDecision.commandReasoning || decision.commandReasoning;
155
+ decision.status = continuedDecision.status;
156
+ decision.statusReasoning = continuedDecision.statusReasoning;
157
+ decision.reasoning = continuedDecision.reasoning;
158
+ }
113
159
  // Execute commands sequentially
114
160
  let iterationHadFailure = false;
115
161
  // Handle blocker if detected (clear blocker FIRST, then proceed with main commands)
116
162
  if (decision.blockerDetected && decision.blockerDetected.clearingCommands && decision.blockerDetected.clearingCommands.length > 0) {
117
163
  this.logger?.(`[Orchestrator] 🚧 BLOCKER DETECTED: ${decision.blockerDetected.description}`);
118
164
  this.logger?.(`[Orchestrator] 🧹 Clearing blocker with ${decision.blockerDetected.clearingCommands.length} command(s)...`);
119
- const blockerResult = await this.executeCommands(decision.blockerDetected.clearingCommands, page, memory, stepNumber, iteration, jobId);
165
+ const urlBeforeBlockerClear = page.url();
166
+ const blockerResult = await this.executeCommands(decision.blockerDetected.clearingCommands, page, memory, stepNumber, iteration, jobId, urlBeforeBlockerClear, decision.screenState);
120
167
  // Add blocker commands with comment to output
121
168
  if (blockerResult.executed.length > 0) {
122
169
  commandsExecuted.push(`// Blocker: ${decision.blockerDetected.description}`);
@@ -131,7 +178,8 @@ class OrchestratorAgent {
131
178
  }
132
179
  // Execute main commands (only if no blocker failure)
133
180
  if (!iterationHadFailure && decision.commands && decision.commands.length > 0) {
134
- const executeResult = await this.executeCommands(decision.commands, page, memory, stepNumber, iteration, jobId);
181
+ const urlBeforeCommands = page.url();
182
+ const executeResult = await this.executeCommands(decision.commands, page, memory, stepNumber, iteration, jobId, urlBeforeCommands, decision.screenState);
135
183
  commandsExecuted.push(...executeResult.executed);
136
184
  // Track failures
137
185
  if (!executeResult.allSucceeded) {
@@ -156,7 +204,8 @@ class OrchestratorAgent {
156
204
  this.logger?.(`[Orchestrator] Generated commands:`);
157
205
  coordCommands.forEach(cmd => this.logger?.(` ${cmd}`));
158
206
  // Execute coordinate commands
159
- const coordResult = await this.executeCommands(coordCommands, page, memory, stepNumber, iteration, jobId);
207
+ const urlBeforeCoord = page.url();
208
+ const coordResult = await this.executeCommands(coordCommands, page, memory, stepNumber, iteration, jobId, urlBeforeCoord, decision.screenState);
160
209
  commandsExecuted.push(...coordResult.executed);
161
210
  if (!coordResult.allSucceeded) {
162
211
  this.logger?.(`[Orchestrator] ❌ Coordinate action failed (Playwright error)`);
@@ -171,6 +220,7 @@ class OrchestratorAgent {
171
220
  iterations: iteration,
172
221
  terminationReason: 'agent_stuck',
173
222
  memory,
223
+ siteLearnings,
174
224
  error: 'Coordinate fallback failed after 2 attempts - unable to proceed'
175
225
  };
176
226
  }
@@ -240,6 +290,7 @@ class OrchestratorAgent {
240
290
  iterations: iteration,
241
291
  terminationReason: 'agent_stuck',
242
292
  memory,
293
+ siteLearnings,
243
294
  error: `Coordinate actions clicked but didn't achieve goal: ${reasoning}`
244
295
  };
245
296
  }
@@ -265,6 +316,7 @@ class OrchestratorAgent {
265
316
  iterations: iteration,
266
317
  terminationReason: 'agent_stuck',
267
318
  memory,
319
+ siteLearnings,
268
320
  error: 'Coordinate fallback failed after 2 attempts - unable to proceed'
269
321
  };
270
322
  }
@@ -280,25 +332,39 @@ class OrchestratorAgent {
280
332
  iterations: iteration,
281
333
  terminationReason: 'agent_stuck',
282
334
  memory,
335
+ siteLearnings,
283
336
  error: `Failed ${consecutiveFailures} iterations in a row - unable to proceed`
284
337
  };
285
338
  }
286
- // Update memory with experiences
287
- if (decision.experiences && decision.experiences.length > 0) {
288
- for (const exp of decision.experiences) {
289
- // Deduplicate - don't add if very similar experience exists
290
- const exists = memory.experiences.some(existing => existing.toLowerCase().includes(exp.toLowerCase()) ||
291
- exp.toLowerCase().includes(existing.toLowerCase()));
292
- if (!exists) {
293
- memory.experiences.push(exp);
294
- this.logger?.(`[Orchestrator] 📚 Experience: ${exp}`);
339
+ // Auto-track visited screen (even without explicit learnings)
340
+ // Filter out transient screens and loading states
341
+ if (decision.screenState) {
342
+ const { screen, state } = decision.screenState;
343
+ // Skip about:blank and loading states (transient, not worth persisting)
344
+ const isTransientScreen = screen === 'about:blank' ||
345
+ screen.toLowerCase().includes('blank');
346
+ const isLoadingState = state.toLowerCase().includes('loading') ||
347
+ state.toLowerCase().includes('spinner') ||
348
+ state.toLowerCase().includes('initializing');
349
+ if (!isTransientScreen && !isLoadingState) {
350
+ if (!siteLearnings.screens[screen]) {
351
+ siteLearnings.screens[screen] = { states: {} };
352
+ this.logger?.(`[📍 Auto-tracked] Screen: ${screen}`);
353
+ }
354
+ if (!siteLearnings.screens[screen].states[state]) {
355
+ siteLearnings.screens[screen].states[state] = { observations: {} };
356
+ this.logger?.(`[📍 Auto-tracked] State: ${screen}[${state}]`);
295
357
  }
296
358
  }
297
- // Cap experiences
298
- if (memory.experiences.length > this.config.maxExperiences) {
299
- memory.experiences = memory.experiences.slice(-this.config.maxExperiences);
359
+ else {
360
+ this.logger?.(`[⏭️ Skipped] Transient screen/state: ${screen}[${state}]`);
300
361
  }
301
362
  }
363
+ // Update site learnings
364
+ if (decision.siteLearningsUpdate) {
365
+ this.logger?.(`[🔍 DEBUG] siteLearningsUpdate from LLM:\n${JSON.stringify(decision.siteLearningsUpdate, null, 2)}`);
366
+ (0, site_learnings_utils_1.mergeSiteLearnings)(siteLearnings, decision.siteLearningsUpdate, this.logger);
367
+ }
302
368
  // Store note to future self (tactical memory across iterations AND steps)
303
369
  if (decision.noteToFutureSelf) {
304
370
  noteToSelf = {
@@ -327,7 +393,8 @@ class OrchestratorAgent {
327
393
  terminationReason: decision.status === 'complete' ? 'complete' :
328
394
  decision.status === 'stuck' ? 'agent_stuck' :
329
395
  'infeasible',
330
- memory
396
+ memory,
397
+ siteLearnings
331
398
  };
332
399
  }
333
400
  }
@@ -340,21 +407,58 @@ class OrchestratorAgent {
340
407
  iterations: iteration,
341
408
  terminationReason: 'system_limit',
342
409
  memory,
410
+ siteLearnings,
343
411
  error: 'Maximum iterations reached'
344
412
  };
345
413
  }
346
414
  /**
347
415
  * Build context for agent
348
416
  */
349
- async buildAgentContext(page, currentStepGoal, stepNumber, totalSteps, scenarioSteps, memory, consecutiveFailures, noteFromPreviousIteration, priorSteps, // For repair mode: prior completed steps
417
+ async buildAgentContext(page, currentStepGoal, stepNumber, totalSteps, scenarioSteps, memory, siteLearnings, // Site learnings (persistent across journeys)
418
+ consecutiveFailures, priorSteps, // For repair mode: prior completed steps
350
419
  nextSteps, // For repair mode: next steps
351
420
  successfulCommandsInStep, // For repair mode: successful commands in THIS step
352
421
  failingCommand, // For repair mode: the failing command
353
422
  remainingCommandsInStep // For repair mode: remaining commands in THIS step
354
423
  ) {
355
- // Get fresh DOM
424
+ // Get fresh DOM (for title only, not displayed in prompts - SoM mode uses visual markers)
356
425
  const currentPageInfo = await (0, page_info_utils_1.getEnhancedPageInfo)(page);
357
426
  const currentURL = page.url();
427
+ // Get page dimensions for scroll vs screenshot decisions
428
+ // IMPORTANT: Wait for page to stabilize and retry until dimensions stop changing (fixes lazy-loaded/dynamic content)
429
+ try {
430
+ await page.waitForLoadState('domcontentloaded', { timeout: 10000 });
431
+ }
432
+ catch (e) {
433
+ // Already loaded, continue
434
+ }
435
+ // Retry approach: Measure scrollHeight multiple times until it stabilizes
436
+ // This handles React/Vue/Angular apps that expand the DOM after initial render
437
+ // Check MULTIPLE sources and use the maximum (handles edge cases like overflow:hidden)
438
+ const measureHeight = `Math.max(
439
+ document.documentElement.scrollHeight || 0,
440
+ document.body.scrollHeight || 0,
441
+ document.documentElement.offsetHeight || 0,
442
+ document.body.offsetHeight || 0
443
+ )`;
444
+ let pageHeight = await page.evaluate(measureHeight).catch(() => 0);
445
+ let previousHeight = 0;
446
+ let attempts = 0;
447
+ while (pageHeight !== previousHeight && attempts < 5) {
448
+ previousHeight = pageHeight;
449
+ await page.waitForTimeout(200); // Wait for potential expansion
450
+ pageHeight = await page.evaluate(measureHeight).catch(() => 0);
451
+ attempts++;
452
+ }
453
+ const viewport = page.viewportSize();
454
+ // @ts-expect-error - document is available in browser context during page.evaluate()
455
+ const pageWidth = await page.evaluate(() => document.documentElement.scrollWidth).catch(() => 0);
456
+ // @ts-expect-error - window is available in browser context during page.evaluate()
457
+ const scrollX = await page.evaluate(() => window.scrollX || window.pageXOffset).catch(() => 0);
458
+ // @ts-expect-error - window is available in browser context during page.evaluate()
459
+ const scrollY = await page.evaluate(() => window.scrollY || window.pageYOffset).catch(() => 0);
460
+ const pageDimensions = { width: pageWidth, height: pageHeight };
461
+ this.logger?.(`[Orchestrator] Page dimensions: ${pageWidth}x${pageHeight}px (viewport: ${viewport?.width}x${viewport?.height}px) - stabilized after ${attempts} checks`, 'log');
358
462
  // Get recent steps
359
463
  const recentSteps = memory.history.slice(-this.config.recentStepsCount);
360
464
  // SoM integration: Update markers and capture screenshot with visual IDs
@@ -375,10 +479,14 @@ class OrchestratorAgent {
375
479
  catch (error) {
376
480
  // Page already loaded or timeout - continue
377
481
  }
378
- // Update SoM markers
379
- await this.somHandler.updateSom();
380
- // Get screenshot WITH markers (viewport only - agent can scroll or use take_screenshot for full page)
381
- somScreenshot = await this.somHandler.getScreenshot(true, false, 60);
482
+ // Update SoM markers - include offscreen elements for full-page screenshots
483
+ await this.somHandler.updateSom(true);
484
+ // TEMPORARY: Always use full-page screenshot for debugging
485
+ // TODO: Re-enable heuristic once we verify full-page works correctly
486
+ const useFullPageSom = true;
487
+ this.logger?.(`[Orchestrator] SoM screenshot strategy: FULL PAGE (ALWAYS) - page: ${pageWidth}x${pageHeight}px, viewport: ${viewport?.width}x${viewport?.height}px`, 'log');
488
+ // Get screenshot WITH markers
489
+ somScreenshot = await this.somHandler.getScreenshot(true, useFullPageSom, 60);
382
490
  // Get element map for disambiguation
383
491
  somElementMap = this.somHandler.getSomElementMap();
384
492
  this.logger?.(`[Orchestrator] SoM screenshot captured for agent decision-making`, 'log');
@@ -395,12 +503,16 @@ class OrchestratorAgent {
395
503
  totalSteps,
396
504
  completedSteps: scenarioSteps.slice(0, stepNumber - 1),
397
505
  remainingSteps: scenarioSteps.slice(stepNumber),
398
- currentPageInfo,
399
506
  currentURL,
400
- recentSteps,
401
- experiences: memory.experiences,
402
- extractedData: memory.extractedData,
403
- noteFromPreviousIteration, // Tactical note from previous iteration
507
+ currentPageTitle: currentPageInfo.title,
508
+ viewportWidth: viewport?.width,
509
+ viewportHeight: viewport?.height,
510
+ pageWidth: pageDimensions.width,
511
+ pageHeight: pageDimensions.height,
512
+ scrollX,
513
+ scrollY,
514
+ journeyMemory: memory, // Journey-specific memory (includes history, extractedData, latestNote)
515
+ siteLearnings, // Site-level learnings (persistent across journeys)
404
516
  somScreenshot, // SoM screenshot with visual markers (current)
405
517
  somElementMap, // SoM element details for disambiguation
406
518
  priorSteps, // Repair: prior completed steps
@@ -458,22 +570,32 @@ class OrchestratorAgent {
458
570
  this.logger?.(`[Orchestrator] Including SoM screenshot in LLM request`, 'log');
459
571
  }
460
572
  const response = await this.llmFacade.llmProvider.callLLM(llmRequest);
461
- // Report token usage
462
- if (response.usage && this.progressReporter?.onTokensUsed) {
463
- const tokenUsage = {
464
- jobId,
465
- stepNumber,
466
- iteration,
467
- inputTokens: response.usage.inputTokens,
468
- outputTokens: response.usage.outputTokens,
469
- includesImage: false,
470
- model: model_constants_1.DEFAULT_MODEL,
471
- timestamp: Date.now()
472
- };
473
- this.logger?.(`[Orchestrator] 💰 Reporting token usage: ${tokenUsage.inputTokens} + ${tokenUsage.outputTokens}`, 'log');
474
- await this.progressReporter.onTokensUsed(tokenUsage);
573
+ // Track token usage and images
574
+ const includesImage = !!context.somScreenshot;
575
+ if (response.usage) {
576
+ // Accumulate in debug stats
577
+ this.debugStats.tokensUsedIn += response.usage.inputTokens;
578
+ this.debugStats.tokensUsedOut += response.usage.outputTokens;
579
+ if (includesImage) {
580
+ this.debugStats.imagesUsed++;
581
+ }
582
+ // Report to progress reporter
583
+ if (this.progressReporter?.onTokensUsed) {
584
+ const tokenUsage = {
585
+ jobId,
586
+ stepNumber,
587
+ iteration,
588
+ inputTokens: response.usage.inputTokens,
589
+ outputTokens: response.usage.outputTokens,
590
+ includesImage,
591
+ model: model_constants_1.DEFAULT_MODEL,
592
+ timestamp: Date.now()
593
+ };
594
+ this.logger?.(`[Orchestrator] 💰 Reporting token usage: ${tokenUsage.inputTokens} + ${tokenUsage.outputTokens}`, 'log');
595
+ await this.progressReporter.onTokensUsed(tokenUsage);
596
+ }
475
597
  }
476
- else if (!response.usage) {
598
+ else {
477
599
  this.logger?.(`[Orchestrator] ⚠ No usage data in LLM response`, 'warn');
478
600
  }
479
601
  // Parse response
@@ -492,7 +614,7 @@ class OrchestratorAgent {
492
614
  /**
493
615
  * Execute tools
494
616
  */
495
- async executeTools(toolCalls, page, memory, stepNumber, refMap) {
617
+ async executeTools(toolCalls, page, memory, stepNumber) {
496
618
  this.logger?.(`[Orchestrator] 🔧 Executing ${toolCalls.length} tool(s)`);
497
619
  const results = {};
498
620
  const toolContext = {
@@ -500,12 +622,23 @@ class OrchestratorAgent {
500
622
  memory,
501
623
  stepNumber,
502
624
  logger: this.logger,
503
- refMap, // Pass refMap for interact_with_ref tool
504
625
  previousSomScreenshot: this.previousSomScreenshot, // For view_previous_screenshot tool
505
626
  somHandler: this.somHandler // For refresh_som_markers tool
506
627
  };
507
628
  for (const toolCall of toolCalls.slice(0, this.config.maxToolCallsPerIteration)) {
508
- this.logger?.(`[Orchestrator] ▶ ${toolCall.name}(${JSON.stringify(toolCall.params).substring(0, 50)}...)`);
629
+ // Log full parameters for debugging (especially for take_screenshot to see if isFullPage is set)
630
+ this.logger?.(`[Orchestrator] ▶ ${toolCall.name}(${JSON.stringify(toolCall.params)})`);
631
+ // Track tool usage in debug stats
632
+ if (!this.debugStats.toolsUsed[toolCall.name]) {
633
+ this.debugStats.toolsUsed[toolCall.name] = {
634
+ count: 0,
635
+ averageUsefulnessScore: 0,
636
+ numTimesRated: 0
637
+ };
638
+ this.logger?.(`[DebugStats] 📊 Tracking new tool: ${toolCall.name}`);
639
+ }
640
+ this.debugStats.toolsUsed[toolCall.name].count++;
641
+ this.logger?.(`[DebugStats] 📊 Tool '${toolCall.name}' used (count: ${this.debugStats.toolsUsed[toolCall.name].count})`);
509
642
  const result = await this.toolRegistry.execute(toolCall, toolContext);
510
643
  results[toolCall.name] = result;
511
644
  if (result.success) {
@@ -549,7 +682,9 @@ class OrchestratorAgent {
549
682
  /**
550
683
  * Execute commands (mix of ref and playwright commands)
551
684
  */
552
- async executeCommands(commands, page, memory, stepNumber, iteration, jobId) {
685
+ async executeCommands(commands, page, memory, stepNumber, iteration, jobId, urlBeforeAction, // URL before commands execute
686
+ screenState // Screen state for memory
687
+ ) {
553
688
  this.logger?.(`[Orchestrator] 📝 Executing ${commands.length} command(s)`);
554
689
  const executed = [];
555
690
  if (commands.length === 0) {
@@ -560,6 +695,11 @@ class OrchestratorAgent {
560
695
  this.logger?.(`[Orchestrator] Using SoM mode for command execution`, 'log');
561
696
  for (let i = 0; i < commands.length; i++) {
562
697
  const cmd = commands[i];
698
+ // Skip if plain string (should not happen in SoM mode, but handle gracefully)
699
+ if (typeof cmd === 'string') {
700
+ this.logger?.(`[Orchestrator] ⚠️ Skipping plain string command in SoM mode: "${cmd}"`, 'warn');
701
+ continue;
702
+ }
563
703
  // Check if verification or action command
564
704
  if ((0, som_types_1.isSomVerification)(cmd)) {
565
705
  // Handle verification command
@@ -580,6 +720,8 @@ class OrchestratorAgent {
580
720
  result: 'success',
581
721
  observation: `Verified: ${cmd.description || cmd.expected}`,
582
722
  url: page.url(),
723
+ previousUrl: urlBeforeAction,
724
+ screenState,
583
725
  timestamp: Date.now()
584
726
  });
585
727
  }
@@ -594,6 +736,8 @@ class OrchestratorAgent {
594
736
  observation: `Failed: ${result.error}`,
595
737
  error: result.error,
596
738
  url: page.url(),
739
+ previousUrl: urlBeforeAction,
740
+ screenState,
597
741
  timestamp: Date.now()
598
742
  });
599
743
  // Continue anyway - verification failures are non-blocking for script generation
@@ -624,6 +768,8 @@ class OrchestratorAgent {
624
768
  result: 'success',
625
769
  observation: 'Executed successfully',
626
770
  url: page.url(),
771
+ previousUrl: urlBeforeAction,
772
+ screenState,
627
773
  timestamp: Date.now()
628
774
  });
629
775
  // Small delay for form validation/animations
@@ -644,12 +790,14 @@ class OrchestratorAgent {
644
790
  observation: `Failed: ${result.error}`,
645
791
  error: result.error,
646
792
  url: page.url(),
793
+ previousUrl: urlBeforeAction,
794
+ screenState,
647
795
  timestamp: Date.now()
648
796
  });
649
797
  // Refresh SoM after batch (DOM may have changed)
650
798
  if (this.somHandler && page) {
651
799
  this.somHandler.setPage(page);
652
- await this.somHandler.updateSom();
800
+ await this.somHandler.updateSom(true);
653
801
  }
654
802
  return { executed, allSucceeded: false };
655
803
  }
@@ -665,12 +813,14 @@ class OrchestratorAgent {
665
813
  observation: `Exception: ${error.message}`,
666
814
  error: error.message,
667
815
  url: page.url(),
816
+ previousUrl: urlBeforeAction,
817
+ screenState,
668
818
  timestamp: Date.now()
669
819
  });
670
820
  // Refresh SoM after batch (DOM may have changed)
671
821
  if (this.somHandler && page) {
672
822
  this.somHandler.setPage(page);
673
- await this.somHandler.updateSom();
823
+ await this.somHandler.updateSom(true);
674
824
  }
675
825
  return { executed, allSucceeded: false };
676
826
  }
@@ -679,19 +829,33 @@ class OrchestratorAgent {
679
829
  this.logger?.(`[Orchestrator] ⚠ [${i + 1}/${commands.length}] Not a valid SoM command/verification, skipping`, 'warn');
680
830
  }
681
831
  }
832
+ // Action-aware stabilization: Detect if commands likely triggered navigation
833
+ const isNavigationAction = (0, page_loading_utils_1.detectNavigationAction)(commands, executed);
682
834
  // Always wait for page to stabilize after command batch
683
835
  // This handles both explicit navigation AND clicks that trigger navigation/SPA routes
684
836
  try {
685
- this.logger?.(`[Orchestrator] Waiting for page to stabilize...`, 'log');
686
- // Use networkidle with short timeout to catch navigation without blocking on SPAs with continuous requests
687
- await page.waitForLoadState('networkidle', { timeout: 3000 });
688
- this.logger?.(`[Orchestrator] Page stabilized (networkidle)`, 'log');
837
+ if (isNavigationAction) {
838
+ this.logger?.(`[Orchestrator] Detected navigation action - using extended wait...`, 'log');
839
+ // Extended wait for form submissions and navigation clicks
840
+ await page.waitForLoadState('networkidle', { timeout: 15000 }); // Longer timeout for slow SPAs
841
+ await page.waitForTimeout(1000); // Initial buffer for SPA rendering
842
+ this.logger?.(`[Orchestrator] Page stabilized after navigation (networkidle + 1s buffer)`, 'log');
843
+ // Smart loading detection: Check if page still shows loading indicators
844
+ await (0, page_loading_utils_1.waitForLoadingToComplete)(page, this.logger);
845
+ }
846
+ else {
847
+ this.logger?.(`[Orchestrator] Waiting for page to stabilize...`, 'log');
848
+ // Use networkidle with short timeout for standard interactions
849
+ await page.waitForLoadState('networkidle', { timeout: 3000 });
850
+ this.logger?.(`[Orchestrator] Page stabilized (networkidle)`, 'log');
851
+ }
689
852
  }
690
853
  catch (error) {
691
- // If networkidle times out, fall back to domcontentloaded
854
+ // If networkidle times out, fall back to domcontentloaded + buffer
692
855
  try {
693
856
  await page.waitForLoadState('domcontentloaded', { timeout: 2000 });
694
- this.logger?.(`[Orchestrator] Page loaded (domcontentloaded)`, 'log');
857
+ await page.waitForTimeout(1000);
858
+ this.logger?.(`[Orchestrator] Page loaded (domcontentloaded + buffer)`, 'log');
695
859
  }
696
860
  catch (error2) {
697
861
  this.logger?.(`[Orchestrator] Page load wait timeout (continuing anyway)`, 'warn');
@@ -700,7 +864,7 @@ class OrchestratorAgent {
700
864
  // Refresh SoM after batch (DOM may have changed and page is now stable)
701
865
  if (this.somHandler && page) {
702
866
  this.somHandler.setPage(page);
703
- await this.somHandler.updateSom();
867
+ await this.somHandler.updateSom(true);
704
868
  }
705
869
  return { executed, allSucceeded: true };
706
870
  }
@@ -733,6 +897,8 @@ try {
733
897
  result: 'success',
734
898
  observation: 'Executed successfully',
735
899
  url: page.url(),
900
+ previousUrl: urlBeforeAction,
901
+ screenState,
736
902
  timestamp: Date.now()
737
903
  });
738
904
  executed.push(cmd);
@@ -755,6 +921,8 @@ try {
755
921
  observation: `Failed: ${errorMessage}`,
756
922
  error: errorMessage,
757
923
  url: page.url(),
924
+ previousUrl: urlBeforeAction,
925
+ screenState,
758
926
  timestamp: Date.now()
759
927
  });
760
928
  return { executed, allSucceeded: false };
@@ -778,7 +946,6 @@ try {
778
946
  agentIteration: iteration,
779
947
  agentReasoning: decision.reasoning,
780
948
  agentSelfReflection: decision.selfReflection,
781
- agentExperiences: decision.experiences,
782
949
  agentToolsUsed: decision.toolCalls?.map(t => t.name),
783
950
  agentStatus: decision.status
784
951
  });
@@ -787,17 +954,25 @@ try {
787
954
  * Execute exploration mode - agent autonomously explores to achieve journey goal
788
955
  * Fires onStepProgress callbacks for each autonomous action (transparent to caller)
789
956
  */
790
- async executeExploration(page, explorationConfig, jobId) {
957
+ async executeExploration(page, explorationConfig, jobId, existingSiteLearnings) {
791
958
  this.logger?.(`\n[Orchestrator] ========== EXPLORATION MODE ==========`);
959
+ this.logger?.(`[Orchestrator] 🚀 runner-core v${package_json_1.version}`);
792
960
  this.logger?.(`[Orchestrator] 🎯 Journey Goal: ${explorationConfig.explorationPrompt}`);
793
961
  if (explorationConfig.testDataPrompt) {
794
962
  this.logger?.(`[Orchestrator] 📋 Test Data: ${explorationConfig.testDataPrompt}`);
795
963
  }
964
+ // Journey memory (temporal)
796
965
  const memory = {
797
966
  history: [],
798
- experiences: [],
799
967
  extractedData: {}
800
968
  };
969
+ // Site learnings (persistent across journeys) - initialize with existing or empty
970
+ const siteLearnings = existingSiteLearnings || { screens: {}, uxPatterns: {} };
971
+ if (existingSiteLearnings) {
972
+ const screenCount = Object.keys(existingSiteLearnings.screens).length;
973
+ const patternCount = Object.keys(existingSiteLearnings.uxPatterns).length;
974
+ this.logger?.(`[Orchestrator] 📚 Loaded existing learnings: ${screenCount} screens, ${patternCount} UX patterns`);
975
+ }
801
976
  const maxSteps = explorationConfig.maxExplorationSteps || 50;
802
977
  let stepNumber = 0;
803
978
  const commandsExecuted = [];
@@ -805,7 +980,7 @@ try {
805
980
  stepNumber++;
806
981
  this.logger?.(`\n[Orchestrator] === Exploration Step ${stepNumber}/${maxSteps} ===`);
807
982
  // Build exploratory context
808
- const context = await this.buildExploratoryContext(page, explorationConfig.explorationPrompt, explorationConfig.testDataPrompt, memory, stepNumber, maxSteps);
983
+ const context = await this.buildExploratoryContext(page, explorationConfig.explorationPrompt, explorationConfig.testDataPrompt, memory, siteLearnings, stepNumber, maxSteps);
809
984
  // Call agent with exploratory prompt
810
985
  const decision = await this.callExploratoryAgent(context, jobId, stepNumber);
811
986
  this.decisionParser.log(decision, stepNumber);
@@ -837,13 +1012,15 @@ try {
837
1012
  // Handle blocker clearing
838
1013
  if (decision.blockerDetected && decision.blockerDetected.clearingCommands) {
839
1014
  this.logger?.(`[Orchestrator] 🚧 Clearing blocker: ${decision.blockerDetected.description}`);
840
- const blockerResult = await this.executeCommands(decision.blockerDetected.clearingCommands, page, memory, stepNumber, 1, jobId);
1015
+ const urlBeforeBlocker = page.url();
1016
+ const blockerResult = await this.executeCommands(decision.blockerDetected.clearingCommands, page, memory, stepNumber, 1, jobId, urlBeforeBlocker, decision.screenState);
841
1017
  commandsExecuted.push(...blockerResult.executed);
842
1018
  }
843
1019
  // Execute exploration commands
844
1020
  let commandsSucceeded = true;
845
1021
  if (decision.commands && decision.commands.length > 0) {
846
- const executeResult = await this.executeCommands(decision.commands, page, memory, stepNumber, 1, jobId);
1022
+ const urlBeforeExploration = page.url();
1023
+ const executeResult = await this.executeCommands(decision.commands, page, memory, stepNumber, 1, jobId, urlBeforeExploration, decision.screenState);
847
1024
  commandsExecuted.push(...executeResult.executed);
848
1025
  commandsSucceeded = executeResult.allSucceeded;
849
1026
  }
@@ -861,13 +1038,35 @@ try {
861
1038
  };
862
1039
  await this.progressReporter.onStepProgress(stepInfo);
863
1040
  }
864
- // Add experiences (both app patterns AND exploration progress)
865
- if (decision.experiences) {
866
- memory.experiences.push(...decision.experiences);
867
- if (memory.experiences.length > this.config.maxExperiences) {
868
- memory.experiences = memory.experiences.slice(-this.config.maxExperiences);
1041
+ // Auto-track visited screen (even without explicit learnings)
1042
+ // Filter out transient screens and loading states
1043
+ if (decision.screenState) {
1044
+ const { screen, state } = decision.screenState;
1045
+ // Skip about:blank and loading states (transient, not worth persisting)
1046
+ const isTransientScreen = screen === 'about:blank' ||
1047
+ screen.toLowerCase().includes('blank');
1048
+ const isLoadingState = state.toLowerCase().includes('loading') ||
1049
+ state.toLowerCase().includes('spinner') ||
1050
+ state.toLowerCase().includes('initializing');
1051
+ if (!isTransientScreen && !isLoadingState) {
1052
+ if (!siteLearnings.screens[screen]) {
1053
+ siteLearnings.screens[screen] = { states: {} };
1054
+ this.logger?.(`[📍 Auto-tracked] Screen: ${screen}`);
1055
+ }
1056
+ if (!siteLearnings.screens[screen].states[state]) {
1057
+ siteLearnings.screens[screen].states[state] = { observations: {} };
1058
+ this.logger?.(`[📍 Auto-tracked] State: ${screen}[${state}]`);
1059
+ }
1060
+ }
1061
+ else {
1062
+ this.logger?.(`[⏭️ Skipped] Transient screen/state: ${screen}[${state}]`);
869
1063
  }
870
1064
  }
1065
+ // Update site learnings
1066
+ if (decision.siteLearningsUpdate) {
1067
+ this.logger?.(`[🔍 DEBUG] siteLearningsUpdate from LLM:\n${JSON.stringify(decision.siteLearningsUpdate, null, 2)}`);
1068
+ (0, site_learnings_utils_1.mergeSiteLearnings)(siteLearnings, decision.siteLearningsUpdate, this.logger);
1069
+ }
871
1070
  // Store note for next iteration
872
1071
  if (decision.noteToFutureSelf) {
873
1072
  memory.latestNote = {
@@ -883,7 +1082,8 @@ try {
883
1082
  commands: commandsExecuted,
884
1083
  iterations: stepNumber,
885
1084
  terminationReason: 'complete',
886
- memory
1085
+ memory,
1086
+ siteLearnings
887
1087
  };
888
1088
  }
889
1089
  else if (decision.status === 'stuck') {
@@ -894,6 +1094,7 @@ try {
894
1094
  iterations: stepNumber,
895
1095
  terminationReason: 'agent_stuck',
896
1096
  memory,
1097
+ siteLearnings,
897
1098
  error: decision.statusReasoning
898
1099
  };
899
1100
  }
@@ -905,14 +1106,47 @@ try {
905
1106
  commands: commandsExecuted,
906
1107
  iterations: stepNumber,
907
1108
  terminationReason: 'system_limit',
908
- memory
1109
+ memory,
1110
+ siteLearnings
909
1111
  };
910
1112
  }
911
- async buildExploratoryContext(page, explorationPrompt, testDataPrompt, memory, stepNumber, maxSteps) {
1113
+ async buildExploratoryContext(page, explorationPrompt, testDataPrompt, memory, siteLearnings, stepNumber, maxSteps) {
912
1114
  // Wait for page to be ready and elements to appear (especially important after navigation)
913
1115
  const currentPageInfo = await page_info_retry_1.PageInfoRetry.getWithRetry(page);
914
1116
  const currentURL = page.url();
915
- const recentSteps = memory.history.slice(-this.config.recentStepsCount);
1117
+ // Get page dimensions for scroll vs screenshot decisions
1118
+ // IMPORTANT: Wait for page to stabilize with retry (fixes lazy-loaded/dynamic content)
1119
+ try {
1120
+ await page.waitForLoadState('domcontentloaded', { timeout: 10000 });
1121
+ }
1122
+ catch (e) {
1123
+ // Already loaded, continue
1124
+ }
1125
+ // Retry approach: Measure scrollHeight multiple times until it stabilizes
1126
+ // Check MULTIPLE sources and use the maximum (handles edge cases like overflow:hidden)
1127
+ const measureHeight = `Math.max(
1128
+ document.documentElement.scrollHeight || 0,
1129
+ document.body.scrollHeight || 0,
1130
+ document.documentElement.offsetHeight || 0,
1131
+ document.body.offsetHeight || 0
1132
+ )`;
1133
+ let pageHeight = await page.evaluate(measureHeight).catch(() => 0);
1134
+ let previousHeight = 0;
1135
+ let attempts = 0;
1136
+ while (pageHeight !== previousHeight && attempts < 5) {
1137
+ previousHeight = pageHeight;
1138
+ await page.waitForTimeout(200); // Wait for potential expansion
1139
+ pageHeight = await page.evaluate(measureHeight).catch(() => 0);
1140
+ attempts++;
1141
+ }
1142
+ const viewport = page.viewportSize();
1143
+ // @ts-expect-error - document is available in browser context during page.evaluate()
1144
+ const pageWidth = await page.evaluate(() => document.documentElement.scrollWidth).catch(() => 0);
1145
+ // @ts-expect-error - window is available in browser context during page.evaluate()
1146
+ const scrollX = await page.evaluate(() => window.scrollX || window.pageXOffset).catch(() => 0);
1147
+ // @ts-expect-error - window is available in browser context during page.evaluate()
1148
+ const scrollY = await page.evaluate(() => window.scrollY || window.pageYOffset).catch(() => 0);
1149
+ this.logger?.(`[Orchestrator] Exploration page dimensions: ${pageWidth}x${pageHeight}px (viewport: ${viewport?.width}x${viewport?.height}px) - stabilized after ${attempts} checks`, 'log');
916
1150
  // SoM integration for exploratory mode
917
1151
  let somScreenshot = undefined;
918
1152
  let somElementMap = undefined;
@@ -926,9 +1160,11 @@ try {
926
1160
  catch (error) {
927
1161
  // Page already loaded or timeout - continue
928
1162
  }
929
- // Update SoM markers
930
- await this.somHandler.updateSom();
931
- somScreenshot = await this.somHandler.getScreenshot(true, false, 60); // Viewport only - agent can scroll or request full page
1163
+ // Update SoM markers after coordinate action
1164
+ await this.somHandler.updateSom(true);
1165
+ // TEMPORARY: Always use full-page screenshot for debugging
1166
+ const useFullPageSom = true;
1167
+ somScreenshot = await this.somHandler.getScreenshot(true, useFullPageSom, 60);
932
1168
  // Get element map for disambiguation
933
1169
  somElementMap = this.somHandler.getSomElementMap();
934
1170
  this.logger?.(`[Orchestrator] SoM screenshot captured for exploratory agent`, 'log');
@@ -944,12 +1180,16 @@ try {
944
1180
  totalSteps: maxSteps,
945
1181
  completedSteps: [],
946
1182
  remainingSteps: [],
947
- currentPageInfo,
948
1183
  currentURL,
949
- recentSteps,
950
- experiences: memory.experiences,
951
- extractedData: memory.extractedData,
952
- noteFromPreviousIteration: memory.latestNote,
1184
+ currentPageTitle: currentPageInfo.title,
1185
+ viewportWidth: viewport?.width,
1186
+ viewportHeight: viewport?.height,
1187
+ pageWidth,
1188
+ pageHeight,
1189
+ scrollX,
1190
+ scrollY,
1191
+ journeyMemory: memory, // Journey-specific memory
1192
+ siteLearnings, // Site-level learnings
953
1193
  testDataPrompt, // CRITICAL: Store testDataPrompt in context
954
1194
  somScreenshot, // SoM screenshot for exploratory mode (current)
955
1195
  somElementMap // SoM element details for disambiguation
@@ -996,6 +1236,20 @@ try {
996
1236
  const decision = this.decisionParser.parse(response.answer);
997
1237
  return decision;
998
1238
  }
1239
+ /**
1240
+ * Get accumulated debug statistics
1241
+ */
1242
+ getDebugStats() {
1243
+ const stats = { ...this.debugStats };
1244
+ // Log summary of collected debug stats
1245
+ this.logger?.(`\n========== DEBUG STATS SUMMARY ==========`);
1246
+ this.logger?.(`Tokens In: ${stats.tokensUsedIn}, Tokens Out: ${stats.tokensUsedOut}`);
1247
+ this.logger?.(`Images Used: ${stats.imagesUsed}`);
1248
+ this.logger?.(`Tools Used: ${Object.keys(stats.toolsUsed).length > 0 ? JSON.stringify(stats.toolsUsed, null, 2) : 'NONE'}`);
1249
+ this.logger?.(`Prompt Suggestions: ${stats.promptImproveSuggestions.length}`);
1250
+ this.logger?.(`=========================================\n`);
1251
+ return stats;
1252
+ }
999
1253
  }
1000
1254
  exports.OrchestratorAgent = OrchestratorAgent;
1001
1255
  //# sourceMappingURL=orchestrator-agent.js.map