@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.
Files changed (28) hide show
  1. package/api/featureSet.js +5 -1
  2. package/api/mablApiClient.js +8 -0
  3. package/commands/branches/branches_cmds/delete.js +63 -0
  4. package/execution/index.js +2 -2
  5. package/mablscript/importer.js +1 -0
  6. package/package.json +1 -1
  7. package/trainer/cli/CliEventHandler.js +69 -2
  8. package/trainer/cli/CliStateProvider.js +5 -1
  9. package/trainer/cli/CliTestGenerationState.js +25 -0
  10. package/trainer/cli/buildCliArtifacts.js +43 -19
  11. package/trainer/cli/runAgenticGeneration.js +15 -5
  12. package/trainer/generation/AgenticGenerationRunner.js +6 -2
  13. package/trainer/generation/TestGenerationOrchestrator.js +12 -15
  14. package/trainer/generation/toolExecutors/CompleteTaskExecutor.js +23 -11
  15. package/trainer/generation/toolExecutors/DeleteLastStepsExecutor.js +2 -1
  16. package/trainer/generation/toolExecutors/RecordAssertElementExecutor.js +3 -2
  17. package/trainer/generation/toolExecutors/RecordAssertionExecutor.js +13 -35
  18. package/trainer/generation/toolExecutors/RecordClickExecutor.js +4 -4
  19. package/trainer/generation/toolExecutors/RecordEnterTextExecutor.js +1 -1
  20. package/trainer/generation/toolExecutors/RecordHoverExecutor.js +1 -1
  21. package/trainer/generation/toolExecutors/RecordStartTaskExecutor.js +24 -4
  22. package/trainer/generation/toolExecutors/ScrollExecutor.js +6 -2
  23. package/trainer/generation/toolExecutors/VisitUrlExecutor.js +1 -1
  24. package/trainer/generation/toolExecutors/WaitExecutor.js +1 -1
  25. package/trainer/sharedUtils/boundingBoxUtils.js +11 -0
  26. package/trainer/sharedUtils/contextBuilder.js +7 -0
  27. package/trainer/sharedUtils/mablscript/extended/ExecuteStepGroupStep.js +3 -0
  28. package/trainer/sharedUtils/messageConversion.js +31 -0
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mablhq/mabl-cli",
3
- "version": "2.94.5",
3
+ "version": "2.96.5",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "The official mabl command line interface tool",
6
6
  "main": "index.js",
@@ -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.state.addSteps([flowStep]);
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
- if (options?.index) {
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 = cursorIndex.length === 1 && cursorIndex[0] === index;
35
+ const isAtCursor = cursorIsAtThisDepth && cursorAtThisDepth === index;
34
36
  if (isAtCursor && !cursorFound) {
35
37
  cursorFound = true;
36
38
  }
37
- const parsedTask = (0, contextBuilder_1.parseTaskFromEchoStep)(step);
38
- if ((0, mablscript_step_types_1.isStepWithReusableFlow)(step) &&
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
- const stepDescription = parsedTask
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
- return { stepList, tasks, currentTaskIndex };
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: cursorIndex[0] ?? 0,
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.steps.length > 0) {
163
+ if (result.rawSteps.length > 0) {
152
164
  console.log(chalk.bold('Generated Steps:'));
153
- result.steps.forEach((step, index) => {
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 = TestGenerationOrchestrator.buildBoundingBox(toolCall.toolParams?.elementBoundingBox);
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 startTaskResult = (0, RecordStartTaskExecutor_1.recordStartTaskStep)(firstTaskDescription);
568
- await this.eventHandler.dispatchSteps(startTaskResult.steps);
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: () => mablApi_1.ExecuteToolName.CompleteTask,
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 { isLastTask, nextTaskDescription, newTaskIndex } = checkTaskStatus(stateProvider, logger);
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 = (0, RecordStartTaskExecutor_1.recordStartTaskStep)(nextTaskDescription);
45
- await eventHandler.dispatchSteps(startTaskResult.steps);
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 = stateProvider.getNextStepIndex();
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.toolParams;
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.toolParams;
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, payload.toolCall.toolParams?.description, stateProvider, eventHandler), 0, stateProvider, eventHandler, logger);
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: (toolCall) => {
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 isElementAssert = !!toolParams.elementBoundingBox;
27
- if (isElementAssert) {
28
- if (!payload.find || !payload.contextInfo) {
29
- logger.error('Record assertion tool executor with element bounding box requires find and context info');
30
- return {
31
- success: false,
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: isElementAssert ? 'element' : 'viewport',
58
- elementScreenshot: isElementAssert,
38
+ selectionType: 'viewport',
39
+ elementScreenshot: false,
59
40
  };
60
41
  const contextInfo = payload.contextInfo ?? (await stateProvider.getActiveContextInfo());
61
- const findDescriptor = isElementAssert && (0, toolExecutors_1.isFindOneEventMessage)(payload.find)
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, payload.toolCall.toolParams?.description, stateProvider, eventHandler), 0, stateProvider, eventHandler, logger);
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) {