@loadmill/droid-cua 2.18.0 → 2.19.0
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.
|
@@ -185,6 +185,19 @@ export class ExecutionEngine {
|
|
|
185
185
|
if (shouldStop()) {
|
|
186
186
|
return { stopped: true, sawExplicitScreenshotAction };
|
|
187
187
|
}
|
|
188
|
+
if (context?.waitIfPaused) {
|
|
189
|
+
const pauseResult = await context.waitIfPaused({
|
|
190
|
+
boundary: "before_action",
|
|
191
|
+
refreshScreenOnResume: true,
|
|
192
|
+
stepContext,
|
|
193
|
+
});
|
|
194
|
+
if (pauseResult?.status === "stopped" || shouldStop()) {
|
|
195
|
+
return { stopped: true, sawExplicitScreenshotAction };
|
|
196
|
+
}
|
|
197
|
+
if (pauseResult?.refreshScreen) {
|
|
198
|
+
return { refreshModelState: true, sawExplicitScreenshotAction };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
188
201
|
if (action.type === "screenshot") {
|
|
189
202
|
sawExplicitScreenshotAction = true;
|
|
190
203
|
continue;
|
|
@@ -223,6 +236,17 @@ export class ExecutionEngine {
|
|
|
223
236
|
}
|
|
224
237
|
return { stopped: false, sawExplicitScreenshotAction };
|
|
225
238
|
}
|
|
239
|
+
async waitIfPaused(context = null, stepContext = null, boundary = "turn_boundary", refreshScreenOnResume = true) {
|
|
240
|
+
if (!context?.waitIfPaused) {
|
|
241
|
+
return { status: "running", refreshScreen: false };
|
|
242
|
+
}
|
|
243
|
+
const result = await context.waitIfPaused({
|
|
244
|
+
boundary,
|
|
245
|
+
refreshScreenOnResume,
|
|
246
|
+
stepContext,
|
|
247
|
+
});
|
|
248
|
+
return result ?? { status: "running", refreshScreen: false };
|
|
249
|
+
}
|
|
226
250
|
async buildActionFollowUp(callId, pendingSafetyChecks, sawExplicitScreenshotAction, addOutput, eventMeta, context = null, stepContext = null) {
|
|
227
251
|
const screenshotBase64 = await this.getScreenshotAsBase64(this.session.deviceId, this.session.deviceInfo);
|
|
228
252
|
emitDesktopDebug("device.screenshot", "device", {
|
|
@@ -358,11 +382,21 @@ export class ExecutionEngine {
|
|
|
358
382
|
instructionIndex: stepContext?.instructionIndex,
|
|
359
383
|
...extra
|
|
360
384
|
});
|
|
385
|
+
let refreshBeforeNextComputerAction = false;
|
|
361
386
|
while (true) {
|
|
362
387
|
// Check for interruption before processing next batch of actions
|
|
363
388
|
if (shouldStop()) {
|
|
389
|
+
summary.stopped = true;
|
|
390
|
+
return finalize();
|
|
391
|
+
}
|
|
392
|
+
const pauseResult = await this.waitIfPaused(context, stepContext, "before_model_output", true);
|
|
393
|
+
if (pauseResult.status === "stopped" || shouldStop()) {
|
|
394
|
+
summary.stopped = true;
|
|
364
395
|
return finalize();
|
|
365
396
|
}
|
|
397
|
+
if (pauseResult.refreshScreen) {
|
|
398
|
+
refreshBeforeNextComputerAction = true;
|
|
399
|
+
}
|
|
366
400
|
const items = response.output || [];
|
|
367
401
|
const actionableItems = extractActionableOutputItems(items);
|
|
368
402
|
const pendingSafetyChecks = collectPendingSafetyChecks(items);
|
|
@@ -385,8 +419,17 @@ export class ExecutionEngine {
|
|
|
385
419
|
currentScreenshot = toolResult.currentScreenshot;
|
|
386
420
|
}
|
|
387
421
|
if (shouldStop()) {
|
|
422
|
+
summary.stopped = true;
|
|
423
|
+
return finalize();
|
|
424
|
+
}
|
|
425
|
+
const toolPauseResult = await this.waitIfPaused(context, stepContext, "after_tool_call", true);
|
|
426
|
+
if (toolPauseResult.status === "stopped" || shouldStop()) {
|
|
427
|
+
summary.stopped = true;
|
|
388
428
|
return finalize();
|
|
389
429
|
}
|
|
430
|
+
if (toolPauseResult.refreshScreen) {
|
|
431
|
+
refreshBeforeNextComputerAction = true;
|
|
432
|
+
}
|
|
390
433
|
if (toolResult.kind === "fail") {
|
|
391
434
|
summary.failed = true;
|
|
392
435
|
summary.error = toolResult.error;
|
|
@@ -419,14 +462,39 @@ export class ExecutionEngine {
|
|
|
419
462
|
continue;
|
|
420
463
|
}
|
|
421
464
|
const { call_id, actions } = actionableItem;
|
|
465
|
+
if (refreshBeforeNextComputerAction) {
|
|
466
|
+
const followUp = await this.buildActionFollowUp(call_id, pendingSafetyChecks, true, addOutput, eventMeta, context, stepContext);
|
|
467
|
+
currentScreenshot = followUp.currentScreenshot ?? currentScreenshot;
|
|
468
|
+
response = await this.requestNextTurn(followUp.input, newResponseId, context, stepContext);
|
|
469
|
+
newResponseId = response.id;
|
|
470
|
+
refreshBeforeNextComputerAction = false;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
422
473
|
const actionBatchResult = await this.executeActionBatch(actions, currentScreenshot, shouldStop, context, stepContext, trackAction);
|
|
423
474
|
if (actionBatchResult.stopped) {
|
|
475
|
+
summary.stopped = true;
|
|
424
476
|
return finalize();
|
|
425
477
|
}
|
|
478
|
+
if (actionBatchResult.refreshModelState) {
|
|
479
|
+
const followUp = await this.buildActionFollowUp(call_id, pendingSafetyChecks, true, addOutput, eventMeta, context, stepContext);
|
|
480
|
+
currentScreenshot = followUp.currentScreenshot ?? currentScreenshot;
|
|
481
|
+
response = await this.requestNextTurn(followUp.input, newResponseId, context, stepContext);
|
|
482
|
+
newResponseId = response.id;
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
426
485
|
const followUp = await this.buildActionFollowUp(call_id, pendingSafetyChecks, actionBatchResult.sawExplicitScreenshotAction, addOutput, eventMeta, context, stepContext);
|
|
427
486
|
if (shouldStop()) {
|
|
487
|
+
summary.stopped = true;
|
|
428
488
|
return finalize();
|
|
429
489
|
}
|
|
490
|
+
const followUpPauseResult = await this.waitIfPaused(context, stepContext, "after_action_follow_up", true);
|
|
491
|
+
if (followUpPauseResult.status === "stopped" || shouldStop()) {
|
|
492
|
+
summary.stopped = true;
|
|
493
|
+
return finalize();
|
|
494
|
+
}
|
|
495
|
+
if (followUpPauseResult.refreshScreen) {
|
|
496
|
+
refreshBeforeNextComputerAction = true;
|
|
497
|
+
}
|
|
430
498
|
currentScreenshot = followUp.currentScreenshot ?? currentScreenshot;
|
|
431
499
|
response = await this.requestNextTurn(followUp.input, newResponseId, context, stepContext);
|
|
432
500
|
newResponseId = response.id;
|
|
@@ -32,6 +32,8 @@ export function mediaKindOf(line) {
|
|
|
32
32
|
return "none";
|
|
33
33
|
}
|
|
34
34
|
export function isHiddenExecutionLogEvent(line) {
|
|
35
|
+
if (line.eventType === "run_paused")
|
|
36
|
+
return true;
|
|
35
37
|
return mediaKindOf(line) === "hidden-debug";
|
|
36
38
|
}
|
|
37
39
|
export function isGalleryMediaEvent(line) {
|
|
@@ -145,6 +145,19 @@ export class ExecutionMode {
|
|
|
145
145
|
});
|
|
146
146
|
return { success: false, error: 'Stopped by user' };
|
|
147
147
|
}
|
|
148
|
+
if (runContext.waitIfPaused) {
|
|
149
|
+
const pauseResult = await runContext.waitIfPaused({
|
|
150
|
+
boundary: 'before_instruction',
|
|
151
|
+
refreshScreenOnResume: false,
|
|
152
|
+
stepContext,
|
|
153
|
+
});
|
|
154
|
+
if (pauseResult?.status === 'stopped' || this.shouldStop) {
|
|
155
|
+
this.emit(addOutput, 'info', 'Test execution stopped by user.', runContext, stepContext, {
|
|
156
|
+
eventType: 'system_message'
|
|
157
|
+
});
|
|
158
|
+
return { success: false, error: 'Stopped by user' };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
148
161
|
const instruction = this.instructions[i];
|
|
149
162
|
this.emit(addOutput, 'user', instruction, runContext, stepContext, {
|
|
150
163
|
eventType: 'instruction_started',
|
|
@@ -256,6 +269,9 @@ export class ExecutionMode {
|
|
|
256
269
|
return this.shouldStop;
|
|
257
270
|
};
|
|
258
271
|
const { responseId: newResponseId, summary: turnSummary } = await this.engine.runFullTurn(response, trackAction, instructionContext, stepContext, { currentScreenshot });
|
|
272
|
+
if (turnSummary.stopped || this.shouldStop) {
|
|
273
|
+
return { success: false, error: "Stopped by user" };
|
|
274
|
+
}
|
|
259
275
|
if (turnSummary.assertionStatus === "passed") {
|
|
260
276
|
this.stats.assertionsPassed++;
|
|
261
277
|
}
|