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.
- package/dist/execution-service.d.ts.map +1 -1
- package/dist/execution-service.js +1 -3
- package/dist/execution-service.js.map +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/decision-parser.d.ts.map +1 -1
- package/dist/orchestrator/decision-parser.js +16 -0
- package/dist/orchestrator/decision-parser.js.map +1 -1
- package/dist/orchestrator/index.d.ts +3 -1
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/index.js +8 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/orchestrator-agent.d.ts +10 -4
- package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator-agent.js +347 -93
- package/dist/orchestrator/orchestrator-agent.js.map +1 -1
- package/dist/orchestrator/orchestrator-prompts.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator-prompts.js +364 -415
- package/dist/orchestrator/orchestrator-prompts.js.map +1 -1
- package/dist/orchestrator/page-loading-utils.d.ts +15 -0
- package/dist/orchestrator/page-loading-utils.d.ts.map +1 -0
- package/dist/orchestrator/page-loading-utils.js +115 -0
- package/dist/orchestrator/page-loading-utils.js.map +1 -0
- package/dist/orchestrator/page-som-handler.d.ts +2 -1
- package/dist/orchestrator/page-som-handler.d.ts.map +1 -1
- package/dist/orchestrator/page-som-handler.js +250 -33
- package/dist/orchestrator/page-som-handler.js.map +1 -1
- package/dist/orchestrator/site-learnings-utils.d.ts +31 -0
- package/dist/orchestrator/site-learnings-utils.d.ts.map +1 -0
- package/dist/orchestrator/site-learnings-utils.js +175 -0
- package/dist/orchestrator/site-learnings-utils.js.map +1 -0
- package/dist/orchestrator/som-types.d.ts +2 -0
- package/dist/orchestrator/som-types.d.ts.map +1 -1
- package/dist/orchestrator/som-types.js.map +1 -1
- package/dist/orchestrator/tools/take-screenshot.d.ts.map +1 -1
- package/dist/orchestrator/tools/take-screenshot.js +10 -1
- package/dist/orchestrator/tools/take-screenshot.js.map +1 -1
- package/dist/orchestrator/types.d.ts +54 -9
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/orchestrator/types.js.map +1 -1
- package/dist/progress-reporter.d.ts +23 -2
- package/dist/progress-reporter.d.ts.map +1 -1
- package/dist/progress-reporter.js.map +1 -1
- package/dist/scenario-service.d.ts +3 -3
- package/dist/scenario-service.d.ts.map +1 -1
- package/dist/scenario-service.js +6 -5
- package/dist/scenario-service.js.map +1 -1
- package/dist/scenario-worker-class.d.ts +7 -3
- package/dist/scenario-worker-class.d.ts.map +1 -1
- package/dist/scenario-worker-class.js +62 -9
- package/dist/scenario-worker-class.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/testing/agent-tester.d.ts +0 -35
- package/dist/testing/agent-tester.d.ts.map +0 -1
- package/dist/testing/agent-tester.js +0 -84
- package/dist/testing/agent-tester.js.map +0 -1
- package/dist/testing/ref-translator-tester.d.ts +0 -44
- package/dist/testing/ref-translator-tester.d.ts.map +0 -1
- package/dist/testing/ref-translator-tester.js +0 -104
- package/dist/testing/ref-translator-tester.js.map +0 -1
- package/dist/utils/hierarchical-selector.d.ts +0 -47
- package/dist/utils/hierarchical-selector.d.ts.map +0 -1
- package/dist/utils/hierarchical-selector.js +0 -212
- package/dist/utils/hierarchical-selector.js.map +0 -1
- package/dist/utils/ref-attacher.d.ts +0 -21
- package/dist/utils/ref-attacher.d.ts.map +0 -1
- package/dist/utils/ref-attacher.js +0 -149
- package/dist/utils/ref-attacher.js.map +0 -1
- package/dist/utils/ref-translator.d.ts +0 -49
- package/dist/utils/ref-translator.d.ts.map +0 -1
- package/dist/utils/ref-translator.js +0 -276
- 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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
298
|
-
|
|
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,
|
|
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
|
-
//
|
|
381
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
//
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
//
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
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
|