Skip to content

Commit abde598

Browse files
cameroncookecodex
andcommitted
fix(ui-automation): Preserve batch action context
Carry the resolved pre-action runtime snapshot into successful batch results. This keeps batch actions aligned with the other UI actions so post-action next-step generation can use the original screen context. Co-Authored-By: Codex <noreply@openai.com>
1 parent 0350033 commit abde598

1 file changed

Lines changed: 21 additions & 5 deletions

File tree

src/mcp/tools/ui-automation/batch.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { captureRuntimeSnapshotAfterActionSafely } from './shared/post-action-sn
1818
import type { AxeHelpers } from './shared/axe-command.ts';
1919
import type { NonStreamingExecutor } from '../../../types/tool-execution.ts';
2020
import type { UiActionResultDomainResult } from '../../../types/domain-results.ts';
21+
import type { RuntimeSnapshotV1 } from '../../../types/ui-snapshot.ts';
2122
import {
2223
createUiActionFailureResult,
2324
createUiActionSuccessResult,
@@ -139,11 +140,17 @@ function buildBatchCommandArgs(params: BatchParams, resolvedSteps: readonly stri
139140
return commandArgs;
140141
}
141142

142-
function resolveBatchSteps(
143-
params: BatchParams,
144-
): { ok: true; steps: string[]; preserveSnapshot: boolean } | { ok: false; result: BatchResult } {
143+
function resolveBatchSteps(params: BatchParams):
144+
| {
145+
ok: true;
146+
steps: string[];
147+
preserveSnapshot: boolean;
148+
previousRuntimeSnapshot: RuntimeSnapshotV1;
149+
}
150+
| { ok: false; result: BatchResult } {
145151
const resolvedSteps: string[] = [];
146152
let preserveSnapshot = true;
153+
let previousRuntimeSnapshot: RuntimeSnapshotV1 | null = null;
147154

148155
for (const step of params.steps) {
149156
const resolution = resolveElementRef(params.simulatorId, step.elementRef, 'tap');
@@ -159,6 +166,8 @@ function resolveBatchSteps(
159166
};
160167
}
161168

169+
previousRuntimeSnapshot ??= resolution.snapshot.payload;
170+
162171
const usesTouchActivation = resolution.element.publicElement.role === 'switch';
163172
preserveSnapshot &&= isSafeSameScreenBatchElement(resolution.element.publicElement);
164173
if (usesTouchActivation && (step.preDelay !== undefined || step.postDelay !== undefined)) {
@@ -200,7 +209,11 @@ function resolveBatchSteps(
200209
resolvedSteps.push(...createSemanticTapBatchSteps(tapCommand));
201210
}
202211

203-
return { ok: true, steps: resolvedSteps, preserveSnapshot };
212+
if (!previousRuntimeSnapshot) {
213+
throw new Error('Batch step resolution succeeded without a runtime snapshot.');
214+
}
215+
216+
return { ok: true, steps: resolvedSteps, preserveSnapshot, previousRuntimeSnapshot };
204217
}
205218

206219
export function createBatchExecutor(
@@ -256,7 +269,9 @@ export function createBatchExecutor(
256269
}
257270

258271
if (resolvedSteps.preserveSnapshot) {
259-
return createUiActionSuccessResult(action, simulatorId, [guard.warningText]);
272+
return createUiActionSuccessResult(action, simulatorId, [guard.warningText], {
273+
previousRuntimeSnapshot: resolvedSteps.previousRuntimeSnapshot,
274+
});
260275
}
261276

262277
const captureResult = await captureRuntimeSnapshotAfterActionSafely({
@@ -269,6 +284,7 @@ export function createBatchExecutor(
269284
simulatorId,
270285
[guard.warningText, captureResult.warning],
271286
{
287+
previousRuntimeSnapshot: resolvedSteps.previousRuntimeSnapshot,
272288
...(captureResult.capture ? { capture: captureResult.capture } : {}),
273289
...(captureResult.uiError ? { uiError: captureResult.uiError } : {}),
274290
},

0 commit comments

Comments
 (0)