@mablhq/mabl-cli 2.94.5 → 2.96.5
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/api/featureSet.js +5 -1
- package/api/mablApiClient.js +8 -0
- package/commands/branches/branches_cmds/delete.js +63 -0
- package/execution/index.js +2 -2
- package/mablscript/importer.js +1 -0
- package/package.json +1 -1
- package/trainer/cli/CliEventHandler.js +69 -2
- package/trainer/cli/CliStateProvider.js +5 -1
- package/trainer/cli/CliTestGenerationState.js +25 -0
- package/trainer/cli/buildCliArtifacts.js +43 -19
- package/trainer/cli/runAgenticGeneration.js +15 -5
- package/trainer/generation/AgenticGenerationRunner.js +6 -2
- package/trainer/generation/TestGenerationOrchestrator.js +12 -15
- package/trainer/generation/toolExecutors/CompleteTaskExecutor.js +23 -11
- package/trainer/generation/toolExecutors/DeleteLastStepsExecutor.js +2 -1
- package/trainer/generation/toolExecutors/RecordAssertElementExecutor.js +3 -2
- package/trainer/generation/toolExecutors/RecordAssertionExecutor.js +13 -35
- package/trainer/generation/toolExecutors/RecordClickExecutor.js +4 -4
- package/trainer/generation/toolExecutors/RecordEnterTextExecutor.js +1 -1
- package/trainer/generation/toolExecutors/RecordHoverExecutor.js +1 -1
- package/trainer/generation/toolExecutors/RecordStartTaskExecutor.js +24 -4
- package/trainer/generation/toolExecutors/ScrollExecutor.js +6 -2
- package/trainer/generation/toolExecutors/VisitUrlExecutor.js +1 -1
- package/trainer/generation/toolExecutors/WaitExecutor.js +1 -1
- package/trainer/sharedUtils/boundingBoxUtils.js +11 -0
- package/trainer/sharedUtils/contextBuilder.js +7 -0
- package/trainer/sharedUtils/mablscript/extended/ExecuteStepGroupStep.js +3 -0
- package/trainer/sharedUtils/messageConversion.js +31 -0
package/mablscript/importer.js
CHANGED
|
@@ -15,6 +15,7 @@ exports.getQueryIds = getQueryIds;
|
|
|
15
15
|
exports.getSnippetIdsFromJsonFlow = getSnippetIdsFromJsonFlow;
|
|
16
16
|
exports.getSnippetIdsFromMablscriptFlow = getSnippetIdsFromMablscriptFlow;
|
|
17
17
|
exports.getSnippetIdsFromApiFlow = getSnippetIdsFromApiFlow;
|
|
18
|
+
exports.getSnippetIdsFromFlow = getSnippetIdsFromFlow;
|
|
18
19
|
exports.getSnippetIdsFromFlows = getSnippetIdsFromFlows;
|
|
19
20
|
const esprima_1 = require("esprima");
|
|
20
21
|
const estraverse_1 = require("estraverse");
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ const mablApi_1 = require("../../mablApi");
|
|
|
8
8
|
const generation_types_1 = require("../sharedUtils/generation-types");
|
|
9
9
|
const mablscript_step_types_1 = require("../sharedUtils/mablscript-step-types");
|
|
10
10
|
const ExecuteFlowStep_1 = __importDefault(require("../sharedUtils/mablscript/extended/ExecuteFlowStep"));
|
|
11
|
+
const ExecuteStepGroupStep_1 = __importDefault(require("../sharedUtils/mablscript/extended/ExecuteStepGroupStep"));
|
|
11
12
|
const importer_1 = require("../../mablscript/importer");
|
|
12
13
|
const chalk = require('chalk');
|
|
13
14
|
class CliEventHandler {
|
|
@@ -97,6 +98,44 @@ class CliEventHandler {
|
|
|
97
98
|
completeTask() {
|
|
98
99
|
console.log('Task completed');
|
|
99
100
|
}
|
|
101
|
+
moveCursorIntoGroup() {
|
|
102
|
+
this.state.moveCursorIntoGroup();
|
|
103
|
+
}
|
|
104
|
+
moveCursorOutOfGroup() {
|
|
105
|
+
this.state.moveCursorOutOfGroup();
|
|
106
|
+
}
|
|
107
|
+
isInsideStepGroup() {
|
|
108
|
+
return this.state.isInsideStepGroup();
|
|
109
|
+
}
|
|
110
|
+
getNextStepIndex() {
|
|
111
|
+
return this.state.getNextStepIndex();
|
|
112
|
+
}
|
|
113
|
+
setNextStepIndex(index) {
|
|
114
|
+
this.state.setNextStepIndex(index);
|
|
115
|
+
}
|
|
116
|
+
getStepAtPath(path) {
|
|
117
|
+
if (path.length === 0) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
let currentSteps = this.state.getSteps();
|
|
121
|
+
let step;
|
|
122
|
+
for (let i = 0; i < path.length; i++) {
|
|
123
|
+
const index = path[i];
|
|
124
|
+
if (index < 0 || index >= currentSteps.length) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
step = currentSteps[index];
|
|
128
|
+
if (i < path.length - 1) {
|
|
129
|
+
if (step instanceof ExecuteStepGroupStep_1.default) {
|
|
130
|
+
currentSteps = step.stepsInGroup;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return step;
|
|
138
|
+
}
|
|
100
139
|
addAgentLogEntry(entry) {
|
|
101
140
|
switch (entry.type) {
|
|
102
141
|
case generation_types_1.AgentLogEntryType.ToolExecuted:
|
|
@@ -186,7 +225,7 @@ class CliEventHandler {
|
|
|
186
225
|
flowInvariantId: flow.invariant_id,
|
|
187
226
|
flow,
|
|
188
227
|
});
|
|
189
|
-
this.
|
|
228
|
+
await this.dispatchSteps([flowStep]);
|
|
190
229
|
}
|
|
191
230
|
try {
|
|
192
231
|
const mablSteps = (0, importer_1.parseMablScriptIntoSteps)(flow);
|
|
@@ -229,7 +268,23 @@ class CliEventHandler {
|
|
|
229
268
|
return Promise.resolve();
|
|
230
269
|
}
|
|
231
270
|
async dispatchSteps(steps, options) {
|
|
232
|
-
|
|
271
|
+
const cursor = this.state.getNextStepIndex();
|
|
272
|
+
if (cursor.length > 1 && !options?.skipActiveGroupInsertion) {
|
|
273
|
+
const containerPath = cursor.slice(0, -1);
|
|
274
|
+
const stepGroup = this.getStepAtPath(containerPath);
|
|
275
|
+
if ((0, mablscript_step_types_1.isStepGroupStep)(stepGroup)) {
|
|
276
|
+
const insertIdx = cursor[cursor.length - 1];
|
|
277
|
+
stepGroup.stepsInGroup.splice(insertIdx, 0, ...steps);
|
|
278
|
+
this.state.setNextStepIndex([
|
|
279
|
+
...containerPath,
|
|
280
|
+
insertIdx + steps.length,
|
|
281
|
+
]);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
this.state.addSteps(steps);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else if (options?.index) {
|
|
233
288
|
this.state.insertStepsAtCursor(steps);
|
|
234
289
|
}
|
|
235
290
|
else {
|
|
@@ -239,6 +294,12 @@ class CliEventHandler {
|
|
|
239
294
|
console.log(` ${options.description}`);
|
|
240
295
|
}
|
|
241
296
|
for (const step of steps) {
|
|
297
|
+
if ((0, mablscript_step_types_1.isStepGroupStep)(step) || (0, mablscript_step_types_1.isStepWithReusableFlow)(step)) {
|
|
298
|
+
if (this.verbose) {
|
|
299
|
+
console.log(` Step ${step.stepType}: container step, skipping execution`);
|
|
300
|
+
}
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
242
303
|
this.resetReplayResponse();
|
|
243
304
|
const result = await this.runSingleStep(step);
|
|
244
305
|
if (this.verbose) {
|
|
@@ -251,6 +312,12 @@ class CliEventHandler {
|
|
|
251
312
|
level: result.level,
|
|
252
313
|
});
|
|
253
314
|
}
|
|
315
|
+
if (options?.skipActiveGroupInsertion && steps.length > 0) {
|
|
316
|
+
const lastStep = steps[steps.length - 1];
|
|
317
|
+
if ((0, mablscript_step_types_1.isStepGroupStep)(lastStep) && lastStep.stepsInGroup.length === 0) {
|
|
318
|
+
this.moveCursorIntoGroup();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
254
321
|
}
|
|
255
322
|
syncVariablesFromExecutionContext() {
|
|
256
323
|
const executionVars = this.stepExecutor.getVariables();
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CliStateProvider = void 0;
|
|
4
4
|
class CliStateProvider {
|
|
5
|
-
constructor(state, trainingContext, flowCache) {
|
|
5
|
+
constructor(state, trainingContext, flowCache, accountFeatures) {
|
|
6
6
|
this.state = state;
|
|
7
7
|
this.trainingContext = trainingContext;
|
|
8
8
|
this.flowCache = flowCache;
|
|
9
|
+
this.accountFeatures = accountFeatures;
|
|
9
10
|
}
|
|
10
11
|
getTestGenerationStatus() {
|
|
11
12
|
return this.state.getStatus();
|
|
@@ -121,5 +122,8 @@ class CliStateProvider {
|
|
|
121
122
|
clearArtifacts() {
|
|
122
123
|
this.state.clearArtifacts();
|
|
123
124
|
}
|
|
125
|
+
hasStepGroupsFeature() {
|
|
126
|
+
return this.accountFeatures?.hasStepGroups() ?? false;
|
|
127
|
+
}
|
|
124
128
|
}
|
|
125
129
|
exports.CliStateProvider = CliStateProvider;
|
|
@@ -104,6 +104,31 @@ class CliTestGenerationState {
|
|
|
104
104
|
setActiveURL(url) {
|
|
105
105
|
this.activeURL = url;
|
|
106
106
|
}
|
|
107
|
+
moveCursorIntoGroup() {
|
|
108
|
+
const lastIndex = this.nextStepIndex[this.nextStepIndex.length - 1];
|
|
109
|
+
if (lastIndex > 0) {
|
|
110
|
+
this.nextStepIndex = [
|
|
111
|
+
...this.nextStepIndex.slice(0, -1),
|
|
112
|
+
lastIndex - 1,
|
|
113
|
+
0,
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
this.nextStepIndex = [...this.nextStepIndex, 0];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
moveCursorOutOfGroup() {
|
|
121
|
+
if (this.nextStepIndex.length > 1) {
|
|
122
|
+
const parentPath = this.nextStepIndex.slice(0, -1);
|
|
123
|
+
this.nextStepIndex = [
|
|
124
|
+
...parentPath.slice(0, -1),
|
|
125
|
+
parentPath[parentPath.length - 1] + 1,
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
isInsideStepGroup() {
|
|
130
|
+
return this.nextStepIndex.length > 1;
|
|
131
|
+
}
|
|
107
132
|
getVariables() {
|
|
108
133
|
return this.variables;
|
|
109
134
|
}
|
|
@@ -8,9 +8,9 @@ exports.buildTestInfoArtifact = buildTestInfoArtifact;
|
|
|
8
8
|
exports.buildAllArtifacts = buildAllArtifacts;
|
|
9
9
|
const variable_utils_1 = require("../sharedUtils/variable-utils");
|
|
10
10
|
const humanize_1 = require("../sharedUtils/mablscript/humanize");
|
|
11
|
-
const mablscript_step_types_1 = require("../sharedUtils/mablscript-step-types");
|
|
12
11
|
const type_utils_1 = require("../sharedUtils/type-utils");
|
|
13
12
|
const contextBuilder_1 = require("../sharedUtils/contextBuilder");
|
|
13
|
+
const mablscript_step_types_1 = require("../sharedUtils/mablscript-step-types");
|
|
14
14
|
const uuid_1 = require("uuid");
|
|
15
15
|
function createJsonArtifact(id, name, description, value) {
|
|
16
16
|
return {
|
|
@@ -24,18 +24,42 @@ function createJsonArtifact(id, name, description, value) {
|
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
function processStepsWithDescriptions(steps, variables, cursorIndex, state) {
|
|
27
|
+
function processStepsWithDescriptions(steps, variables, cursorIndex, state, depth = 0) {
|
|
28
28
|
const stepList = [];
|
|
29
29
|
const tasks = [];
|
|
30
30
|
let currentTaskIndex = -1;
|
|
31
31
|
let cursorFound = false;
|
|
32
|
+
const cursorAtThisDepth = cursorIndex[depth];
|
|
33
|
+
const cursorIsAtThisDepth = cursorIndex.length === depth + 1;
|
|
32
34
|
steps.forEach((step, index) => {
|
|
33
|
-
const isAtCursor =
|
|
35
|
+
const isAtCursor = cursorIsAtThisDepth && cursorAtThisDepth === index;
|
|
34
36
|
if (isAtCursor && !cursorFound) {
|
|
35
37
|
cursorFound = true;
|
|
36
38
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
if ((0, mablscript_step_types_1.isStepGroupStep)(step)) {
|
|
40
|
+
const groupDescription = step.description ?? 'Unnamed Step Group';
|
|
41
|
+
stepList.push(`Start task: ${groupDescription}`);
|
|
42
|
+
tasks.push(groupDescription);
|
|
43
|
+
const taskIsBeforeCursor = cursorAtThisDepth > index;
|
|
44
|
+
if (taskIsBeforeCursor) {
|
|
45
|
+
currentTaskIndex++;
|
|
46
|
+
}
|
|
47
|
+
const cursorIsInsideThisGroup = cursorIndex.length > depth + 1 && cursorAtThisDepth === index;
|
|
48
|
+
if (cursorIsInsideThisGroup) {
|
|
49
|
+
const nestedResult = processStepsWithDescriptions(step.stepsInGroup, variables, cursorIndex, state, depth + 1);
|
|
50
|
+
stepList.push(...nestedResult.stepList);
|
|
51
|
+
if (nestedResult.cursorFound) {
|
|
52
|
+
cursorFound = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
step.stepsInGroup.forEach((nestedStep) => {
|
|
57
|
+
stepList.push((0, humanize_1.humanizeStepDescription)(nestedStep, variables));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
stepList.push(`End task: ${groupDescription}`);
|
|
61
|
+
}
|
|
62
|
+
else if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step) &&
|
|
39
63
|
step.flow &&
|
|
40
64
|
!(0, type_utils_1.isAutoLoginFlow)(step.flow)) {
|
|
41
65
|
const flowName = step.flow.description ?? 'Unknown flow';
|
|
@@ -48,20 +72,16 @@ function processStepsWithDescriptions(steps, variables, cursorIndex, state) {
|
|
|
48
72
|
stepList.push(`End execute flow: ${flowName}`);
|
|
49
73
|
}
|
|
50
74
|
else {
|
|
51
|
-
|
|
52
|
-
? `${parsedTask}`
|
|
53
|
-
: (0, humanize_1.humanizeStepDescription)(step, variables);
|
|
54
|
-
stepList.push(stepDescription);
|
|
55
|
-
}
|
|
56
|
-
if (parsedTask) {
|
|
57
|
-
tasks.push(parsedTask);
|
|
58
|
-
const wasBeforeCursor = !cursorFound && !isAtCursor;
|
|
59
|
-
if (wasBeforeCursor) {
|
|
60
|
-
currentTaskIndex++;
|
|
61
|
-
}
|
|
75
|
+
stepList.push((0, humanize_1.humanizeStepDescription)(step, variables));
|
|
62
76
|
}
|
|
63
77
|
});
|
|
64
|
-
|
|
78
|
+
if (!cursorFound &&
|
|
79
|
+
cursorIsAtThisDepth &&
|
|
80
|
+
cursorAtThisDepth === steps.length) {
|
|
81
|
+
cursorFound = true;
|
|
82
|
+
stepList.push(contextBuilder_1.CURSOR_MARKER);
|
|
83
|
+
}
|
|
84
|
+
return { stepList, tasks, currentTaskIndex, cursorFound };
|
|
65
85
|
}
|
|
66
86
|
function buildVariablesMap(variables, cursorIndex) {
|
|
67
87
|
const variablesList = (0, variable_utils_1.getUsableVariablesAsList)(variables, cursorIndex);
|
|
@@ -80,16 +100,20 @@ function buildTestExecutionStateArtifact(options) {
|
|
|
80
100
|
const steps = state.getSteps();
|
|
81
101
|
const variables = state.getVariables();
|
|
82
102
|
const variablesMap = buildVariablesMap(variables, cursorIndex);
|
|
83
|
-
const { stepList, tasks, currentTaskIndex } = processStepsWithDescriptions(steps, variables, cursorIndex, state);
|
|
103
|
+
const { stepList, tasks, currentTaskIndex, cursorFound } = processStepsWithDescriptions(steps, variables, cursorIndex, state);
|
|
104
|
+
if (!cursorFound) {
|
|
105
|
+
stepList.push(contextBuilder_1.CURSOR_MARKER);
|
|
106
|
+
}
|
|
84
107
|
const lastToolsExecuted = state.getLastToolsExecuted();
|
|
85
108
|
const lastTaskStatus = lastToolsExecuted?.map((tool) => ({
|
|
86
109
|
success: tool.toolResult?.success ?? false,
|
|
87
110
|
steps_created: tool.toolResult?.createdStepsInfo?.map((info) => info.humanizedStep.step) ?? [],
|
|
88
111
|
action: tool.toolCall?.toolName,
|
|
89
112
|
})) ?? [];
|
|
113
|
+
const flattenedCursorPosition = stepList.indexOf(contextBuilder_1.CURSOR_MARKER);
|
|
90
114
|
const testExecutionState = {
|
|
91
115
|
fullStepList: stepList,
|
|
92
|
-
cursorPosition:
|
|
116
|
+
cursorPosition: flattenedCursorPosition >= 0 ? flattenedCursorPosition : stepList.length,
|
|
93
117
|
currentTaskIndex: tasks.length === 0 ? 0 : currentTaskIndex,
|
|
94
118
|
variables: variablesMap,
|
|
95
119
|
lastTaskStatus,
|
|
@@ -47,6 +47,8 @@ const CliEventHandler_1 = require("./CliEventHandler");
|
|
|
47
47
|
const ViewportStep_1 = __importDefault(require("../sharedUtils/mablscript/canonical/ViewportStep"));
|
|
48
48
|
const VisitUrlStep_1 = __importDefault(require("../sharedUtils/mablscript/canonical/VisitUrlStep"));
|
|
49
49
|
const domUtil_1 = require("../../domUtil");
|
|
50
|
+
const mablscript_step_types_1 = require("../sharedUtils/mablscript-step-types");
|
|
51
|
+
const humanize_1 = require("../sharedUtils/mablscript/humanize");
|
|
50
52
|
const sessionHelpers_1 = require("./sessionHelpers");
|
|
51
53
|
const chalk = require('chalk');
|
|
52
54
|
function createReadlineInterface() {
|
|
@@ -62,6 +64,16 @@ function printStartupInfo(resolvedSession, headless) {
|
|
|
62
64
|
console.log(chalk.gray(' Headless:'), chalk.white(String(headless)));
|
|
63
65
|
console.log();
|
|
64
66
|
}
|
|
67
|
+
function printStepsRecursively(steps, variables, prefix = '', indent = ' ') {
|
|
68
|
+
steps.forEach((step, index) => {
|
|
69
|
+
const stepNumber = prefix ? `${prefix}.${index + 1}` : `${index + 1}`;
|
|
70
|
+
const description = step.annotations?.description || (0, humanize_1.humanizeStepDescription)(step, variables);
|
|
71
|
+
console.log(chalk.gray(`${indent}${stepNumber}.`), chalk.white(description));
|
|
72
|
+
if ((0, mablscript_step_types_1.isStepGroupStep)(step) && step.stepsInGroup.length > 0) {
|
|
73
|
+
printStepsRecursively(step.stepsInGroup, variables, stepNumber, indent + ' ');
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
65
77
|
function createResultProcessor() {
|
|
66
78
|
return (result) => {
|
|
67
79
|
const isError = result.status === 'failed';
|
|
@@ -135,7 +147,7 @@ async function runAgenticGeneration(options) {
|
|
|
135
147
|
return state;
|
|
136
148
|
},
|
|
137
149
|
createTrainingContext: (page, browser, onNewTabDetected) => new CliTrainingContext_1.CliTrainingContext({ page, browser, onNewTabDetected }),
|
|
138
|
-
createStateProvider: (state, trainingContext, flowCache) => new CliStateProvider_1.CliStateProvider(state, trainingContext, flowCache),
|
|
150
|
+
createStateProvider: (state, trainingContext, flowCache, accountFeatures) => new CliStateProvider_1.CliStateProvider(state, trainingContext, flowCache, accountFeatures),
|
|
139
151
|
createEventHandler: (opts) => new CliEventHandler_1.CliEventHandler({
|
|
140
152
|
state: opts.state,
|
|
141
153
|
trainingContext: opts.trainingContext,
|
|
@@ -148,11 +160,9 @@ async function runAgenticGeneration(options) {
|
|
|
148
160
|
processResult: createResultProcessor(),
|
|
149
161
|
});
|
|
150
162
|
const result = await runner.run();
|
|
151
|
-
if (result.
|
|
163
|
+
if (result.rawSteps.length > 0) {
|
|
152
164
|
console.log(chalk.bold('Generated Steps:'));
|
|
153
|
-
result.
|
|
154
|
-
console.log(chalk.gray(` ${index + 1}.`), chalk.white(step));
|
|
155
|
-
});
|
|
165
|
+
printStepsRecursively(result.rawSteps, result.variables);
|
|
156
166
|
console.log();
|
|
157
167
|
}
|
|
158
168
|
if (result.saveResult) {
|
|
@@ -102,13 +102,14 @@ class AgenticGenerationRunner {
|
|
|
102
102
|
}
|
|
103
103
|
async createBrowserAndNavigate() {
|
|
104
104
|
this.logger.info('Creating browser and navigating to URL');
|
|
105
|
-
const { testContext } = await (0, createTrainingTestContext_1.createTrainingTestContext)({
|
|
105
|
+
const { testContext, accountFeatures } = await (0, createTrainingTestContext_1.createTrainingTestContext)({
|
|
106
106
|
workspaceId: this.config.workspaceId,
|
|
107
107
|
applicationId: this.config.applicationId,
|
|
108
108
|
url: this.config.url,
|
|
109
109
|
apiClient: this.config.apiClient,
|
|
110
110
|
idPrefix: 'gen',
|
|
111
111
|
});
|
|
112
|
+
this.accountFeatures = accountFeatures;
|
|
112
113
|
const browserConfig = {
|
|
113
114
|
height: 900,
|
|
114
115
|
width: 1280,
|
|
@@ -156,7 +157,7 @@ class AgenticGenerationRunner {
|
|
|
156
157
|
});
|
|
157
158
|
});
|
|
158
159
|
await this.trainingContext.initialize();
|
|
159
|
-
this.stateProvider = this.providers.createStateProvider(this.state, this.trainingContext, this.flowCache);
|
|
160
|
+
this.stateProvider = this.providers.createStateProvider(this.state, this.trainingContext, this.flowCache, this.accountFeatures);
|
|
160
161
|
this.eventHandler = this.providers.createEventHandler({
|
|
161
162
|
state: this.state,
|
|
162
163
|
trainingContext: this.trainingContext,
|
|
@@ -243,6 +244,7 @@ class AgenticGenerationRunner {
|
|
|
243
244
|
trainingSessionId: loopResult.trainingSessionId,
|
|
244
245
|
status: loopResult.status,
|
|
245
246
|
rawSteps: loopResult.steps,
|
|
247
|
+
variables: loopResult.variables,
|
|
246
248
|
error: loopResult.pendingAskForHelp.question,
|
|
247
249
|
};
|
|
248
250
|
}
|
|
@@ -255,6 +257,7 @@ class AgenticGenerationRunner {
|
|
|
255
257
|
trainingSessionId: loopResult.trainingSessionId,
|
|
256
258
|
status: loopResult.status,
|
|
257
259
|
rawSteps: loopResult.steps,
|
|
260
|
+
variables: loopResult.variables,
|
|
258
261
|
error: loopResult.status === generation_types_1.TestGenerationStatus.Failed
|
|
259
262
|
? 'Generation failed'
|
|
260
263
|
: undefined,
|
|
@@ -270,6 +273,7 @@ class AgenticGenerationRunner {
|
|
|
270
273
|
trainingSessionId: this.state?.getTrainingSessionId() ?? '',
|
|
271
274
|
status: generation_types_1.TestGenerationStatus.Failed,
|
|
272
275
|
rawSteps: [],
|
|
276
|
+
variables: this.state?.getVariables() ?? {},
|
|
273
277
|
};
|
|
274
278
|
}
|
|
275
279
|
async cleanup() {
|
|
@@ -7,6 +7,7 @@ exports.TestGenerationOrchestrator = void 0;
|
|
|
7
7
|
const async_retry_1 = __importDefault(require("async-retry"));
|
|
8
8
|
const mablApi_1 = require("../../mablApi");
|
|
9
9
|
const toolExecutors_1 = require("./toolExecutors");
|
|
10
|
+
const boundingBoxUtils_1 = require("../sharedUtils/boundingBoxUtils");
|
|
10
11
|
const contentActions_1 = require("../background/capture/contentActions");
|
|
11
12
|
const generation_types_1 = require("../sharedUtils/generation-types");
|
|
12
13
|
const messageConversion_1 = require("../sharedUtils/messageConversion");
|
|
@@ -252,7 +253,9 @@ class TestGenerationOrchestrator {
|
|
|
252
253
|
};
|
|
253
254
|
this.logger.info('[runGenerationLoop] Tool call information', JSON.stringify(toolCall, undefined, 2));
|
|
254
255
|
if (toolExecutor.needsFind(toolCall)) {
|
|
255
|
-
const boundingBox =
|
|
256
|
+
const boundingBox = toolCall.toolParams?.elementBoundingBox
|
|
257
|
+
? (0, boundingBoxUtils_1.convertToolLibraryBoundingBox)(toolCall.toolParams.elementBoundingBox)
|
|
258
|
+
: undefined;
|
|
256
259
|
if (!boundingBox) {
|
|
257
260
|
this.logger.error(`Tool ${originalToolCall.name} requires element location but no bounding box was provided by the API`);
|
|
258
261
|
skippedTools.push({
|
|
@@ -452,17 +455,6 @@ class TestGenerationOrchestrator {
|
|
|
452
455
|
return true;
|
|
453
456
|
}
|
|
454
457
|
}
|
|
455
|
-
static buildBoundingBox(responseBoundingBox) {
|
|
456
|
-
if (!responseBoundingBox) {
|
|
457
|
-
return undefined;
|
|
458
|
-
}
|
|
459
|
-
return {
|
|
460
|
-
x: responseBoundingBox.left,
|
|
461
|
-
y: responseBoundingBox.top,
|
|
462
|
-
width: responseBoundingBox.right - responseBoundingBox.left,
|
|
463
|
-
height: responseBoundingBox.bottom - responseBoundingBox.top,
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
458
|
async captureToolOutputContext(success, toolName, tabsBefore = []) {
|
|
467
459
|
const { needsScreenshot, needsUrl, needsTabInfo } = (0, toolExecutors_1.toolNeedsPostExecutionContext)(toolName, success);
|
|
468
460
|
const context = {};
|
|
@@ -522,7 +514,7 @@ class TestGenerationOrchestrator {
|
|
|
522
514
|
const cursorPosition = cursorIndex[0];
|
|
523
515
|
let hasStartTaskBeforeCursor = false;
|
|
524
516
|
for (let i = 0; i < cursorPosition && i < steps.length; i++) {
|
|
525
|
-
const parsedTask = (0, contextBuilder_1.parseTaskFromEchoStep)(steps[i]);
|
|
517
|
+
const parsedTask = (0, contextBuilder_1.parseTaskFromStep)(steps[i]) ?? (0, contextBuilder_1.parseTaskFromEchoStep)(steps[i]);
|
|
526
518
|
if (parsedTask) {
|
|
527
519
|
hasStartTaskBeforeCursor = true;
|
|
528
520
|
break;
|
|
@@ -564,8 +556,13 @@ class TestGenerationOrchestrator {
|
|
|
564
556
|
return;
|
|
565
557
|
}
|
|
566
558
|
this.logger.info(`Adding initial start task step: ${firstTaskDescription}`);
|
|
567
|
-
const
|
|
568
|
-
|
|
559
|
+
const useStepGroups = this.stateProvider.hasStepGroupsFeature();
|
|
560
|
+
const startTaskResult = useStepGroups
|
|
561
|
+
? (0, RecordStartTaskExecutor_1.recordStartTaskStep)(firstTaskDescription)
|
|
562
|
+
: (0, RecordStartTaskExecutor_1.recordStartTaskEchoStep)(firstTaskDescription);
|
|
563
|
+
await this.eventHandler.dispatchSteps(startTaskResult.steps, {
|
|
564
|
+
skipActiveGroupInsertion: useStepGroups,
|
|
565
|
+
});
|
|
569
566
|
}
|
|
570
567
|
}
|
|
571
568
|
exports.TestGenerationOrchestrator = TestGenerationOrchestrator;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.completeTaskExecutor = void 0;
|
|
4
|
-
const mablApi_1 = require("../../../mablApi");
|
|
5
4
|
const RecordStartTaskExecutor_1 = require("./RecordStartTaskExecutor");
|
|
6
5
|
const contextBuilder_1 = require("../../sharedUtils/contextBuilder");
|
|
7
6
|
exports.completeTaskExecutor = {
|
|
8
7
|
needsFind: () => false,
|
|
9
|
-
getStepType: () =>
|
|
8
|
+
getStepType: () => 'complete_task',
|
|
10
9
|
execute: async (payload, stateProvider, eventHandler, logger) => {
|
|
11
10
|
const lastToolsExecuted = stateProvider.getLastToolsExecuted();
|
|
12
11
|
const someStepFailed = lastToolsExecuted?.some((tool) => !!tool.generatedSteps && !tool.toolResult.success);
|
|
@@ -17,7 +16,11 @@ exports.completeTaskExecutor = {
|
|
|
17
16
|
errorMessage: 'Cannot complete task because a generated step failed.',
|
|
18
17
|
});
|
|
19
18
|
}
|
|
20
|
-
const
|
|
19
|
+
const useStepGroups = stateProvider.hasStepGroupsFeature();
|
|
20
|
+
if (useStepGroups) {
|
|
21
|
+
eventHandler.moveCursorOutOfGroup();
|
|
22
|
+
}
|
|
23
|
+
const { isLastTask, nextTaskDescription, newTaskIndex } = checkTaskStatus(stateProvider, eventHandler, logger);
|
|
21
24
|
eventHandler.completeTask();
|
|
22
25
|
const stopCondition = payload.session.stopCondition;
|
|
23
26
|
if (stopCondition === 'generated-task') {
|
|
@@ -41,8 +44,17 @@ exports.completeTaskExecutor = {
|
|
|
41
44
|
if (nextTaskDescription) {
|
|
42
45
|
logger.info(`Creating start task step for next task: ${nextTaskDescription}`);
|
|
43
46
|
try {
|
|
44
|
-
const startTaskResult =
|
|
45
|
-
|
|
47
|
+
const startTaskResult = useStepGroups
|
|
48
|
+
? (0, RecordStartTaskExecutor_1.recordStartTaskStep)(nextTaskDescription)
|
|
49
|
+
: (0, RecordStartTaskExecutor_1.recordStartTaskEchoStep)(nextTaskDescription);
|
|
50
|
+
await eventHandler.dispatchSteps(startTaskResult.steps, {
|
|
51
|
+
skipActiveGroupInsertion: useStepGroups,
|
|
52
|
+
});
|
|
53
|
+
if (useStepGroups) {
|
|
54
|
+
if (!eventHandler.isInsideStepGroup()) {
|
|
55
|
+
eventHandler.moveCursorIntoGroup();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
46
58
|
}
|
|
47
59
|
catch (error) {
|
|
48
60
|
logger.error('Error creating next start task step:', error);
|
|
@@ -59,7 +71,7 @@ exports.completeTaskExecutor = {
|
|
|
59
71
|
});
|
|
60
72
|
},
|
|
61
73
|
};
|
|
62
|
-
function checkTaskStatus(stateProvider, logger) {
|
|
74
|
+
function checkTaskStatus(stateProvider, eventHandler, logger) {
|
|
63
75
|
const testOutlineArtifact = stateProvider.getArtifact('test_outline');
|
|
64
76
|
const simpleOutlineArtifact = stateProvider.getArtifact('simple_test_outline');
|
|
65
77
|
const hasTestOutline = testOutlineArtifact && testOutlineArtifact.artifact_type === 'inline';
|
|
@@ -71,33 +83,33 @@ function checkTaskStatus(stateProvider, logger) {
|
|
|
71
83
|
if (hasTestOutline) {
|
|
72
84
|
const data = testOutlineArtifact.artifact_data?.value;
|
|
73
85
|
if (data?.tasks) {
|
|
74
|
-
return getTaskInfo(data, stateProvider, logger);
|
|
86
|
+
return getTaskInfo(data, stateProvider, eventHandler, logger);
|
|
75
87
|
}
|
|
76
88
|
logger.warn('test_outline artifact exists but has no data or tasks');
|
|
77
89
|
}
|
|
78
90
|
if (hasSimpleOutline) {
|
|
79
91
|
const data = simpleOutlineArtifact.artifact_data?.value;
|
|
80
92
|
if (data?.tasks) {
|
|
81
|
-
return getTaskInfo(data, stateProvider, logger);
|
|
93
|
+
return getTaskInfo(data, stateProvider, eventHandler, logger);
|
|
82
94
|
}
|
|
83
95
|
logger.warn('simple_test_outline artifact exists but has no data or tasks');
|
|
84
96
|
}
|
|
85
97
|
logger.warn('Outline artifacts found but contain no valid task data, assuming not last task');
|
|
86
98
|
return { isLastTask: false };
|
|
87
99
|
}
|
|
88
|
-
function getTaskInfo(outline, stateProvider, logger) {
|
|
100
|
+
function getTaskInfo(outline, stateProvider, eventHandler, logger) {
|
|
89
101
|
if (!outline?.tasks || outline.tasks.length === 0) {
|
|
90
102
|
logger.info('No tasks in outline, not ending test');
|
|
91
103
|
return { isLastTask: false };
|
|
92
104
|
}
|
|
93
105
|
const steps = stateProvider.getSteps();
|
|
94
|
-
const cursorIndex =
|
|
106
|
+
const cursorIndex = eventHandler.getNextStepIndex();
|
|
95
107
|
let currentTaskIndex = 0;
|
|
96
108
|
if (cursorIndex.length === 1) {
|
|
97
109
|
const cursorPosition = cursorIndex[0];
|
|
98
110
|
let taskCount = 0;
|
|
99
111
|
for (let i = 0; i < cursorPosition && i < steps.length; i++) {
|
|
100
|
-
const parsedTask = (0, contextBuilder_1.parseTaskFromEchoStep)(steps[i]);
|
|
112
|
+
const parsedTask = (0, contextBuilder_1.parseTaskFromStep)(steps[i]) ?? (0, contextBuilder_1.parseTaskFromEchoStep)(steps[i]);
|
|
101
113
|
if (parsedTask) {
|
|
102
114
|
taskCount++;
|
|
103
115
|
}
|
|
@@ -6,7 +6,8 @@ exports.deleteLastStepsExecutor = {
|
|
|
6
6
|
needsFind: () => false,
|
|
7
7
|
getStepType: () => mablApi_1.ExecuteToolName.DeleteLastSteps,
|
|
8
8
|
execute: (payload, _stateProvider, eventHandler, logger) => {
|
|
9
|
-
const toolParams = payload.toolCall
|
|
9
|
+
const toolParams = payload.toolCall
|
|
10
|
+
.toolParams;
|
|
10
11
|
const { stepsToDelete } = toolParams;
|
|
11
12
|
if (typeof stepsToDelete !== 'number' || stepsToDelete <= 0) {
|
|
12
13
|
logger.error('Invalid stepsToDelete for RecordDeleteLastStepsExecutor');
|
|
@@ -9,7 +9,8 @@ exports.recordAssertElementExecutor = {
|
|
|
9
9
|
needsFind: () => true,
|
|
10
10
|
getStepType: () => 'assert',
|
|
11
11
|
execute: async (payload, stateProvider, eventHandler, logger) => {
|
|
12
|
-
const toolParams = payload.toolCall
|
|
12
|
+
const toolParams = payload.toolCall
|
|
13
|
+
.toolParams;
|
|
13
14
|
if (!payload.find || !payload.contextInfo) {
|
|
14
15
|
logger.error('RecordAssertElementExecutor requires find and contextInfo');
|
|
15
16
|
return {
|
|
@@ -37,6 +38,6 @@ exports.recordAssertElementExecutor = {
|
|
|
37
38
|
elementAttrProp: 'innerText',
|
|
38
39
|
},
|
|
39
40
|
};
|
|
40
|
-
return (0, toolExecutors_1.recordStepAndTrackResult)(() => (0, stepCapture_1.recordTrainedStep)(step, payload.contextInfo, true,
|
|
41
|
+
return (0, toolExecutors_1.recordStepAndTrackResult)(() => (0, stepCapture_1.recordTrainedStep)(step, payload.contextInfo, true, undefined, stateProvider, eventHandler), 0, stateProvider, eventHandler, logger);
|
|
41
42
|
},
|
|
42
43
|
};
|
|
@@ -6,14 +6,9 @@ const generation_types_1 = require("../../sharedUtils/generation-types");
|
|
|
6
6
|
const ExtractDescriptor_1 = require("../../../mablscript/types/ExtractDescriptor");
|
|
7
7
|
const AssertStep_1 = require("../../../mablscript/steps/AssertStep");
|
|
8
8
|
const stepExecution_1 = require("../../sharedUtils/stepExecution");
|
|
9
|
-
const translateMablscript_1 = require("../../sharedUtils/capture/translateMablscript");
|
|
10
|
-
const mablscriptFind_1 = require("../../../mablscriptFind");
|
|
11
9
|
const stepCapture_1 = require("../../background/capture/stepCapture");
|
|
12
10
|
exports.recordAssertionExecutor = {
|
|
13
|
-
needsFind: (
|
|
14
|
-
const toolParams = toolCall.toolParams;
|
|
15
|
-
return isAssertToolParams(toolParams) && !!toolParams.elementBoundingBox;
|
|
16
|
-
},
|
|
11
|
+
needsFind: () => false,
|
|
17
12
|
getStepType: () => 'assert',
|
|
18
13
|
execute: async (payload, stateProvider, eventHandler, logger) => {
|
|
19
14
|
const toolParams = payload.toolCall.toolParams;
|
|
@@ -23,28 +18,14 @@ exports.recordAssertionExecutor = {
|
|
|
23
18
|
success: false,
|
|
24
19
|
};
|
|
25
20
|
}
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
if ((0, toolExecutors_1.isFindOneEventMessage)(payload.find)) {
|
|
36
|
-
delete payload.find.stepType;
|
|
37
|
-
}
|
|
38
|
-
const step = (0, toolExecutors_1.isFindOneEventMessage)(payload.find)
|
|
39
|
-
? { ...payload.find, listener: 'mouse-select' }
|
|
40
|
-
: {
|
|
41
|
-
selectionType: 'viewport',
|
|
42
|
-
listener: 'viewport-assert',
|
|
43
|
-
findDescriptor: {
|
|
44
|
-
type: 'viewport',
|
|
45
|
-
},
|
|
46
|
-
action: 'record',
|
|
47
|
-
};
|
|
21
|
+
const step = {
|
|
22
|
+
selectionType: 'viewport',
|
|
23
|
+
listener: 'viewport-assert',
|
|
24
|
+
findDescriptor: {
|
|
25
|
+
type: 'viewport',
|
|
26
|
+
},
|
|
27
|
+
action: 'record',
|
|
28
|
+
};
|
|
48
29
|
step.assertion = {
|
|
49
30
|
assertionType: 'ai_prompt',
|
|
50
31
|
assertionTarget: 'element',
|
|
@@ -54,14 +35,11 @@ exports.recordAssertionExecutor = {
|
|
|
54
35
|
},
|
|
55
36
|
extractionType: ExtractDescriptor_1.ExtractType.EXTRACT_PROMPT,
|
|
56
37
|
onFailure: AssertStep_1.OnFailure.FailTestAtEnd,
|
|
57
|
-
selectionType:
|
|
58
|
-
elementScreenshot:
|
|
38
|
+
selectionType: 'viewport',
|
|
39
|
+
elementScreenshot: false,
|
|
59
40
|
};
|
|
60
41
|
const contextInfo = payload.contextInfo ?? (await stateProvider.getActiveContextInfo());
|
|
61
|
-
const findDescriptor =
|
|
62
|
-
?
|
|
63
|
-
(0, translateMablscript_1.createFindDescriptor)(payload.find, new mablscriptFind_1.ElementSelectorStore())
|
|
64
|
-
: { type: 'viewport' };
|
|
42
|
+
const findDescriptor = { type: 'viewport' };
|
|
65
43
|
const variables = stateProvider.getVariables();
|
|
66
44
|
const testAssertionResult = await (0, stepExecution_1.runAIAssertion)(eventHandler, variables, findDescriptor, step.assertion, contextInfo.frameId, `${contextInfo.tabId}`);
|
|
67
45
|
step.assertion.aiPrompt.criteria =
|
|
@@ -74,7 +52,7 @@ exports.recordAssertionExecutor = {
|
|
|
74
52
|
success: false,
|
|
75
53
|
};
|
|
76
54
|
}
|
|
77
|
-
return (0, toolExecutors_1.recordStepAndTrackResult)(() => (0, stepCapture_1.recordTrainedStep)(step, contextInfo, true,
|
|
55
|
+
return (0, toolExecutors_1.recordStepAndTrackResult)(() => (0, stepCapture_1.recordTrainedStep)(step, contextInfo, true, undefined, stateProvider, eventHandler), 0, stateProvider, eventHandler, logger);
|
|
78
56
|
},
|
|
79
57
|
};
|
|
80
58
|
function isAssertToolParams(toolParams) {
|