JavaScript API
Repeato allows you to execute JavaScript using Script steps. The following globals are available while a test or batch is running.
For machine-readable documentation, use the raw Markdown reference or the TypeScript declarations. See JavaScript recipes for complete, executable examples.
Globals
Section titled “Globals”Type: object
An object that you can use to share data between steps.
data.scannedText
Section titled “data.scannedText”Type: string
The string scanned by the last executed Check for text step.
workspaceDir
Section titled “workspaceDir”Type: string
The directory of the current workspace.
currentStepIndex
Section titled “currentStepIndex”Type: number
The zero-based index of the currently executing step.
testDir
Section titled “testDir”Type: string
The directory of the current test.
testRunDir
Section titled “testRunDir”Type: string
The directory of the current test run.
Type: AxiosInstance
The Axios HTTP client.
const response = await axios.get('https://example.com')log(message: object)Logs a message or object.
sleep()
Section titled “sleep()”sleep(milliseconds: number): Promise<void>Delays execution for the given number of milliseconds.
getResponse()
Section titled “getResponse()”getResponse(host: string, path: string, headers?: object): Promise<string>Sends a GET request to a host.
sendPost()
Section titled “sendPost()”sendPost( host: string, path: string, body: string | object, acceptHeader?: string, headers?: object,): Promise<string>Sends a POST request.
showDialog()
Section titled “showDialog()”showDialog({ title, content, onConfirm, onClose,}: { title: string content: string onConfirm?: () => void onClose?: () => void}): { cancel: Function; confirm: Function }Shows a dialog whose content supports Markdown, including formatted text, links, and images. The optional callbacks run when the corresponding action occurs. The returned dialog instance can be closed using cancel() or confirm().
batchRunner
Section titled “batchRunner”Controls the current test batch and provides batch lifecycle hooks.
batchRunner.testBatch
Section titled “batchRunner.testBatch”Type: TestBatch
The current batch of tests.
batchRunner.currentExceptionHandlerIndex
Section titled “batchRunner.currentExceptionHandlerIndex”Type: number
The index of the current exception handler.
batchRunner.currentTestIndex
Section titled “batchRunner.currentTestIndex”Type: number
The index of the current test.
batchRunner.batchRun
Section titled “batchRunner.batchRun”Type: BatchRun
The current batch run. Use log(batchRunner.batchRun) to inspect all properties.
batchRunner.isPlaying
Section titled “batchRunner.isPlaying”Type: boolean
Indicates whether the batch runner is currently running.
batchRunner.runMode
Section titled “batchRunner.runMode”Type: string
The current run mode.
batchRunner.stop()
Section titled “batchRunner.stop()”stop(): Promise<void>Stops the batch runner.
batchRunner.createBatchRunExport()
Section titled “batchRunner.createBatchRunExport()”createBatchRunExport(): Promise<string>Creates an export of the current batch run in the workspace’s batchRuns folder and returns its path.
batchRunner.uploadReport()
Section titled “batchRunner.uploadReport()”uploadReport(dirPath: string): Promise<string>Uploads the report at dirPath to the Repeato cloud and returns the report URL.
batchRunner.setTestBatchById()
Section titled “batchRunner.setTestBatchById()”batchRunner.setTestBatchById(batchId: string)Sets the test batch for the batch runner.
batchRunner.setRunMode()
Section titled “batchRunner.setRunMode()”batchRunner.setRunMode(mode: string)Sets the run mode to all tests or failed tests only.
batchRunner.setRunMode('AllTests')batchRunner.setRunMode('OnlyFailed')batchRunner.addOnBatchStart()
Section titled “batchRunner.addOnBatchStart()”batchRunner.addOnBatchStart(key: string, callback: (batch: Batch) => void)Registers a callback that runs just before the batch starts.
batchRunner.addOnTestFail()
Section titled “batchRunner.addOnTestFail()”batchRunner.addOnTestFail( key: string, callback: (test: Test, testRun: TestRun, stepResult: StepResult) => void,)Registers a callback that runs when a test fails.
batchRunner.addOnTestSuccess()
Section titled “batchRunner.addOnTestSuccess()”batchRunner.addOnTestSuccess( key: string, callback: (test: Test, testRun: TestRun) => void,)Registers a callback that runs when a test succeeds.
batchRunner.addOnBatchCompleted()
Section titled “batchRunner.addOnBatchCompleted()”batchRunner.addOnBatchCompleted( key: string, callback: (batchRun: BatchRun) => void,)Registers a callback that runs when the batch completes, whether it succeeds or fails.
batchRunner.removeOnBatchStart()
Section titled “batchRunner.removeOnBatchStart()”batchRunner.removeOnBatchStart(key: string)Removes a callback registered with addOnBatchStart().
batchRunner.removeOnTestFail()
Section titled “batchRunner.removeOnTestFail()”batchRunner.removeOnTestFail(key: string)Removes a callback registered with addOnTestFail().
batchRunner.removeOnTestSuccess()
Section titled “batchRunner.removeOnTestSuccess()”batchRunner.removeOnTestSuccess(key: string)Removes a callback registered with addOnTestSuccess().
batchRunner.removeOnBatchCompleted()
Section titled “batchRunner.removeOnBatchCompleted()”batchRunner.removeOnBatchCompleted(key: string)Removes a callback registered with addOnBatchCompleted().
batchRunner.testRunner
Section titled “batchRunner.testRunner”Type: TestRunner
Provides access to all testRunner properties and methods. Use log(batchRunner.testRunner) to inspect them.
testRunner
Section titled “testRunner”Controls execution of the current test and exposes step lifecycle hooks.
testRunner.currentTest
Section titled “testRunner.currentTest”Type: Test
The current test. Use log(testRunner.currentTest) to inspect all properties.
testRunner.currentTestRun
Section titled “testRunner.currentTestRun”Type: TestRun
The current test run. Use log(testRunner.currentTestRun) to inspect all properties.
testRunner.currentTestRun.partial is a synchronous, read-only boolean on all platforms in Script steps and Advanced configuration callbacks. It is true for single-step and Run to here execution, with no device interaction or other side effects. Partial runs keep their step results but cannot be successful or become reference runs; wasSuccessful remains false. Older saved runs default to partial: false.
testRunner.lastStepResult
Section titled “testRunner.lastStepResult”Type: StepResult
The result of the previously executed step. Use log(testRunner.lastStepResult) to inspect all properties.
Click semantic agreement evidence
Section titled “Click semantic agreement evidence”In Script steps and Advanced configuration callbacks, step.semanticAgreement is a synchronous, read-only click configuration flag (false by default; absent for other step types). Configure it through Studio’s click editor or MCP. Enabled clicks require a fresh hierarchy with a unique visible semantic target containing the proposed tap point; failure rejects the tap, and devices without usable hierarchy/locators cannot establish agreement.
stepResult.semanticAgreement is a synchronous read with no side effects. It is present when the guard evaluated a target and contains agreed, reason, candidateCount, and optional target bounds, resolved locators, or an ambiguous locatorType. Bounds use normalized coordinates from 0 to 1. Missing evidence does not mean agreement passed. This records geometric agreement, not actual event delivery or business outcomes.
const agreement = testRunner.lastStepResult?.semanticAgreementif (agreement) log(agreement)Click execution evidence and warnings
Section titled “Click execution evidence and warnings”In Script steps and Advanced configuration, stepResult.executionEvidence is a synchronous, read-only optional property available on newly executed matching/click results before step-completed callbacks. It returns stored execution facts or undefined on older results, with no device side effects. It includes the effective searchedRegion, requestedLocators, matchSource, optional resolvedLocator, matchedBounds, actual attempted tapPoint, and dispatch timestamps/status. Bounds and tap points use normalized full-frame coordinates from 0 to 1; source-frame width/height are processed image pixels. Coordinate clicks have no search region or resolved element bounds.
sourceFrame identifies a screenshot associated with the result. Its phase distinguishes the exact worker-input matching_frame from a screenshot read after_locator_resolution (which is not the hierarchy used to resolve the locator). receivedAt is an ISO client receive/read timestamp, not a provider capture timestamp; matchedAt records worker completion when known. Screenshots are filenames relative to the normal run directory, retrieved through the run’s screenshot API or MCP artifact metadata. Missing fields remain null. Matching buffers are not stored inline in the result.
tapStatus is not_dispatched, dispatching, completed (the driver command resolved), or unknown (a command rejected after dispatch may have partially applied). A completed dispatch does not prove event delivery or the desired app outcome. A matching frame is historical; the physical screen can change before a tap. No automatic retry is performed on an uncertain dispatch.
stepResult.warnings is a read-only array of warning codes. A successful locator fallback retains success: true and adds fingerprint_fallback_used; warnings no longer suppress its tap. Optional failed steps can still have success: undefined. Locator-derived bounds use their center without applying a recorded fingerprint offset; fingerprint/OCR clicks retain their configured offset behavior.
const result = testRunner.lastStepResultif (result?.executionEvidence) { log(result.executionEvidence) log(result.warnings)}Stored failure hierarchy
Section titled “Stored failure hierarchy”In Script steps and Advanced configuration, stepResult.failureEvidence is an optional, read-only persisted value. Reading it is synchronous, returns the stored evidence or undefined, and has no device side effects. Studio captures it after failed steps (including optional failures), after addOnStepCompleted callbacks finish. Those callbacks do not yet see this capture; later Script steps or test-failure callbacks can inspect it when execution continues. Successful and older results may omit it.
failureEvidence.hierarchy contains status (captured, unavailable, or capture_failed), data (the full JSON-serializable driver hierarchy or null), and capturedAt (an ISO timestamp or null). Capture requires a connected device with hierarchy support and has a three-second deadline. Missing hierarchy support, disconnection, timeout, or serialization errors do not fail the test further. Capture occurs after the failed action, separately from its screenshot. An empty hierarchy is distinct from unavailable data.
Evidence also includes classification, search stages when available, and capture timing. Unknown keyboard visibility, IME bounds, scroll offset, foreground package, and tap delivery remain unknown. Hierarchy attributes and bounds retain driver-specific structure and units; they are not normalized 0–1 touch coordinates. Hierarchies can contain entered text. Evidence stays in the normal run structure. Each test retains at most five runs, protecting the reference run; failed/interrupted results and screenshots remain until their run is evicted. Manual exports can keep a copy longer. MCP repeato_get_step_result returns the complete result JSON, including this hierarchy, from retained test runs.
const evidence = testRunner.lastStepResult?.failureEvidenceif (evidence?.hierarchy.status === 'captured') { log(evidence.hierarchy.data)}testRunner.currentStepIndex
Section titled “testRunner.currentStepIndex”Type: number
The zero-based index of the current step.
testRunner.setNextStepId()
Section titled “testRunner.setNextStepId()”testRunner.setNextStepId(id: string)Sets the next step to execute by ID.
testRunner.setNextStepId('AH23D6')testRunner.setNextStepIndex()
Section titled “testRunner.setNextStepIndex()”testRunner.setNextStepIndex(index: number)Sets the next step to execute by its zero-based index.
testRunner.goBackBy()
Section titled “testRunner.goBackBy()”testRunner.goBackBy(stepCount: number)Moves back by stepCount steps and continues execution from there.
testRunner.addOnScreenshotSaved()
Section titled “testRunner.addOnScreenshotSaved()”testRunner.addOnScreenshotSaved( key: string, listener: (stepResult: StepResult, screenshotPath: string) => void,)Registers a listener that runs as soon as a screenshot has been saved.
testRunner.addOnStepCompleted()
Section titled “testRunner.addOnStepCompleted()”testRunner.addOnStepCompleted( key: string, callback: (stepResult: StepResult) => void,)Registers a callback that runs whenever a step completes.
testRunner.removeOnScreenshotSaved()
Section titled “testRunner.removeOnScreenshotSaved()”testRunner.removeOnScreenshotSaved(key: string)Removes a listener registered with addOnScreenshotSaved().
testRunner.removeOnStepCompleted()
Section titled “testRunner.removeOnStepCompleted()”testRunner.removeOnStepCompleted(key: string)Removes a callback registered with addOnStepCompleted().
testRunner.setScaleInvariantMatchingEnabled()
Section titled “testRunner.setScaleInvariantMatchingEnabled()”testRunner.setScaleInvariantMatchingEnabled(enabled: boolean)Enables or disables scale-invariant matching. It is enabled by default.
deviceConnector
Section titled “deviceConnector”Provides information about and sends commands to the currently connected device.
deviceConnector.selectedDeviceData
Section titled “deviceConnector.selectedDeviceData”Type: DeviceData
All data related to the currently connected device. Use log(deviceConnector.selectedDeviceData) to inspect it.
deviceConnector.sendString()
Section titled “deviceConnector.sendString()”sendString(value: string): Promise<void>Sends a string to the device. The app under test must have a focused input field to receive the data.
deviceConnector.clearText()
Section titled “deviceConnector.clearText()”clearText(): Promise<void>Available in Script steps and Advanced configuration with a connected device and a focused input. Asynchronously clears that field and resolves with no value; it does not type replacement text.
Cloud Android uses the input’s long-press selection menu and Select all, followed by Backspace and an empty-field check. It requires one inspectable focused EditText and an available selection control; no ADB is used. Cloud iOS clearing is unsupported. Missing focus, unavailable controls, unsupported devices, and provider timeouts reject the promise. A failure may leave input or selection state changed, so inspect the screen before retrying.
await deviceConnector.clearText()await deviceConnector.sendString('Replacement text')deviceConnector.sendClick()
Section titled “deviceConnector.sendClick()”sendClick(xPercentage: number, yPercentage: number): Promise<void>Sends a touch event to the device. Coordinates range from 0 to 1; sendClick(0.5, 0.5) taps the center of the screen.
deviceConnector.sendDown()
Section titled “deviceConnector.sendDown()”sendDown(xPercentage: number, yPercentage: number): Promise<void>Sends a touch-down event. Each sendDown() needs a sendUp(); combine it with sendMove() to simulate a drag gesture.
deviceConnector.sendMove()
Section titled “deviceConnector.sendMove()”sendMove(xPercentage: number, yPercentage: number): Promise<void>Sends a move event. Use sendDown() first and finish the drag with sendUp().
deviceConnector.sendUp()
Section titled “deviceConnector.sendUp()”sendUp(xPercentage: number, yPercentage: number): Promise<void>Sends a touch-up event.
deviceConnector.sendAdbCommand()
Section titled “deviceConnector.sendAdbCommand()”sendAdbCommand(command: string): Promise<string>Sends an Android Debug Bridge (ADB) command.
deviceConnector.addOnDeviceLog()
Section titled “deviceConnector.addOnDeviceLog()”deviceConnector.addOnDeviceLog( listenerName: string, callback: (log: string) => void,)Registers a callback that runs for every log line sent by the app under test.
deviceConnector.sendIdbCommand()
Section titled “deviceConnector.sendIdbCommand()”sendIdbCommand(command: string): Promise<string>Sends an iOS Development Bridge (IDB) command.
await deviceConnector.sendIdbCommand('list-targets')deviceConnector.takeScreenshot()
Section titled “deviceConnector.takeScreenshot()”takeScreenshot(filepath: string): Promise<void>Takes a screenshot and saves it at filepath.
deviceConnector.setClipboard()
Section titled “deviceConnector.setClipboard()”setClipboard(content: string): Promise<void>Sets the device clipboard to a string.
deviceConnector.getClipboard()
Section titled “deviceConnector.getClipboard()”getClipboard(): Promise<string>Gets the device clipboard content.
deviceConnector.vision()
Section titled “deviceConnector.vision()”vision( prompt: string, region: { x: number; y: number; width: number; height: number } | undefined,): Promise<string>Sends a prompt and a screenshot of the current device screen to the vision module. Pass a region to limit the screenshot to part of the screen.
audioTools
Section titled “audioTools”Provides text-to-speech, audio playback, and audio recording.
audioTools.say()
Section titled “audioTools.say()”audioTools.say(text: string)Turns the given text into audio.
audioTools.playAudio()
Section titled “audioTools.playAudio()”playAudio(url: string): Promise<number>Plays audio from the given URL.
audioTools.stopAudio()
Section titled “audioTools.stopAudio()”audioTools.stopAudio()Stops the currently playing audio.
audioTools.recordAudio()
Section titled “audioTools.recordAudio()”recordAudio(duration: number): Promise<string>Records audio for the given duration in milliseconds and returns the URL of the temporary recording.
OCR and locator execution evidence
Section titled “OCR and locator execution evidence”testRunner.lastStepResult.executionEvidence is a synchronous read-only snapshot. Its optional ocr object retains the latest completed recognizedText, including when comparison fails, along with the engine, expected text, operator, completed-frame count and completion time. Status distinguishes no_frame_processed, no_completed_frame, engine_error, no_text, no_match and matched. Missing completed output is null, while completed recognition of no text is an empty string. The source-frame reference identifies the inspected screenshot when available. Late worker completion does not update an already returned timeout result. OCR settling consumes at most one quarter of its search budget or one second, leaving the remaining time for recognition. OCR timing and recognition accuracy depend on the engine and device content.
executionEvidence.locatorResults records hierarchy locator candidates and rejection reasons. Native hierarchy fallbacks require one enabled actionable control inside the configured normalized search region; static labels, ambiguous controls and known keyboard-covered tap points are rejected. Android supports an exact hint locator. Missing keyboard evidence remains unknown. Matching and completed input dispatch do not prove the app outcome.
Text steps can configure expectedFocusHint in Studio or MCP. Before clearing or typing during recording and replay, this optional guard asynchronously reads the device hierarchy and requires exactly one focused editable field with that exact hint. It fails before text mutation when the hierarchy is unavailable or focus disagrees. It does not change focus. step.expectedFocusHint itself is a synchronous read of the configured expectation. Native Android exposes hints where supplied by the app/provider; other platforms may not expose them.
Navigation step evidence
Section titled “Navigation step evidence”In Script steps and Advanced configuration, Navigation steps expose synchronous read-only goal, startCondition, inputReferences and maxActions properties. Other step types omit them. The optional starting condition must be verified before any action. Input references select existing data fields. Navigation sends the selected names and resolved values to the provider in state.inputData and saved in decision evidence. Do not select secrets. Values typed by the runtime still come from supplied data and are not generated by the model. maxActions accepts 1–100 and defaults to 20. Navigation requires a device with hierarchy and screenshots, a configured OpenRouter key, and the supported decision model. The inherited step timeout is its total execution budget in seconds (default 60, maximum 300).
After execution, stepResult.executionEvidence.navigation is a synchronous, read-only optional object available before completion callbacks. It has no device side effects and is absent on other/older results. It contains model (the pinned resolved model), requestedModel, status, elapsedMs, actionCount, modelCalls, and trace. Each trace entry includes the operation, dynamic target number, status, elapsed milliseconds, optional goalConfidence, operationConfidence, visibilityDecision (VISIBLE/HIDDEN/UNCERTAIN), visibilityConfidence, inputReference, and the exact observation screenshot filename in the test-run directory. Target numbers apply only to that observation; they are not reusable locators. The matching evidence source is navigation; individual actions are recorded in the navigation trace rather than the top-level tap fields.
A goal judgment, timeout, cancellation, invalid starting condition, unsafe target, unstable screen, lack of progress or exhausted budget ends the step without automatically retrying the test. Goal recognition is a model judgment; retain a separate ordinary assertion for the final business outcome. Hierarchy geometry uses normalized coordinates from 0 to 1. Screenshots and visible page content are sent to the configured model provider, so use demo data for benchmarks and keep secrets out of goals.
Multiple results from Navigation
Section titled “Multiple results from Navigation”In Script steps and Advanced configuration on Studio and CLI, testRun.stepResults is a chronological flat list. A Navigation step can produce several real results with the same step.id: informational observations followed by one final result. Read these fields synchronously; reading them has no side effects. resultKind is intermediate or final; older loaded results default to final. Optional executionId groups one execution, sequence orders its results, and elapsedMs measures milliseconds since that Navigation execution started. Intermediate executionEvidence.navigationObservation records operation, status, target and optional error/reference details. Its screenshot is the pre-action model observation, not an after-action confirmation. screenshot is an optional filename relative to the test-run directory; an unavailable image is not synthesized.
testRun.getStepResultsByStepId(stepId) synchronously returns all results for that step in recorded order, without changing the run. getStepResultByStepId(stepId) returns the latest final result or undefined before completion. getSuccessfulStepResults() includes successful final results only. testRunner.lastStepResult and step-completed callbacks still refer to the final outcome, not intermediate decisions. Intermediate results do not increase successful, failed or optional-step counts. Existing historical runs retain the original single result; the viewer does not convert legacy JSON traces into invented results.
For new Navigation observations, executionEvidence.navigationObservation.decisionInput is a synchronous, read-only snapshot of the structured model request: state, questions, model/provider and saved screenshot reference. Reading it has no side effects. It excludes authorization and inline image bytes, and is absent in older runs. Only explicitly selected inputReferences are resolved and included as the name-to-string-value map decisionInput.state.inputData. These values are transmitted to the configured navigation provider and retained in execution evidence; do not select secrets. The entire data object and unrelated fields are excluded. The screenshot can also show visible field contents. Independent expected-result assertions are not included. The Batch Run Viewer displays this snapshot in an expandable Decision input region. The final trace omits duplicate input payloads; inspect the actual intermediate results for each decision.
New intermediate Navigation results expose optional executionEvidence.navigationObservation.decisionOutput: the exact received model JSON body, saved before local response validation. It is a synchronous read-only snapshot on Studio and CLI, also available through MCP run/result tools; reading it has no side effects. No authorization headers are stored. Older results and requests without a received JSON response omit it; missing outputs are never reconstructed from the executed action. The viewer displays it with the same expandable tree as Decision input. The final trace omits duplicate input/output bodies.
Optional decisionMetrics records local requestDurationMs (fetch through JSON decoding, excluding image encoding), requestBytes (UTF-8 JSON body including base64 image, excluding HTTP headers), structuredBytes (UTF-8 serialization of model, structured state, questions and provider without the image), and imageBytes (JPEG/PNG bytes before base64). Sizes are not token usage or billed cost. Actual usage, when supplied by the provider, remains in decisionOutput. These fields share the same availability and read-only behavior as decisionOutput.
Navigation sends an incoming JPEG screenshot unchanged with its matching JPEG media type; other frames retain PNG normalization. This does not resize the image or add lossy compression. The saved observation remains the exact source frame.
Navigation completion currently requires model goal probability at least 0.70; an explicit start condition still requires 0.85 on the first decision. This shared Studio/CLI/MCP execution policy does not establish task success independently. Retain explicit outcome assertions, especially for booking, saving or expected error states. The probabilities are model judgments, not a guarantee. The completion-policy experiment and regressions are tracked in Studio’s iteration log.
Navigation TYPE_TEXT decisions choose the editable target and named data reference together in one choice. The runner validates the pair against its current choices, verifies focus and, when a focus click is needed, target visibility or driver reachability, and resolves the supplied value locally before typing. The selected reference names and expected string values are also transmitted in decisionInput.state.inputData and retained in evidence; unrelated test data is excluded. The persisted observation still exposes the candidate ID in target and the existing data path in inputReference; older model outputs used separate target and input heads. Reading this evidence is synchronous and has no side effects. Joint selection does not guarantee that the chosen field/reference pair is semantically correct.
Navigation currently accepts at most 250 locally filtered visible candidates, including static text and actionable controls. Larger observations fail explicitly without silently truncating targets. This is an execution resource limit, not a minimum number of elements or a model requirement. Existing timeout/action budgets still apply.
Native GPT-6 Luna Decisions adapter
Section titled “Native GPT-6 Luna Decisions adapter”Set REPEATO_NAVIGATION_MODEL=gpt-6-luna and OPENAI_API_KEY before starting Studio/CLI/MCP. Jev remains the default. GPT-6 Luna uses POST https://api.openai.com/v1/decisions, with the same filtered state, original screenshot and question meanings. Legacy internal noul questions map to native predicate questions; fixed criteria map to native choice values/descriptions. No Responses JSON generation, reasoning setting, output-token limit, generated coordinates/text values or model fallback is used by this adapter. Existing cancellation/deadline, visibility, freshness, focus, budgets and independent outcome assertions remain applicable.
Raw navigationObservation.decisionOutput retains the native API body before validation. The adapter matches the answers array by each unique question name, validates types/probability ranges/allowed values, and maps predicate probability into the existing goal/start checks (0.70/0.85 unchanged). Required refusal, duplicate/missing/unknown names and invalid choices fail without dispatch. Confidence/probabilities come from the API rather than invented values. Different models’ probability calibration is not assumed equivalent. Any returned model must match gpt-6-luna; if the API omits model identity, resolved model remains null rather than being fabricated.
decisionInput.api is openai-decisions; requestOptions.questions records the exact mapped wire questions, while state and the screenshot reference reconstruct shared evidence. The Jev-only provider field is absent. Reads are synchronous and have no side effects. For GPT, structuredBytes serializes the native request with an empty image URL; image bytes are reported separately. Missing provider cost is unknown, not zero. Existing historical openai-responses reports remain unmodified and must not be labeled native Decisions runs.
The official Decisions guide documents text/image inputs and predicate/choice responses. Live model/account availability still requires a successful preflight. All execution evidence remains accessible through the existing MCP run/result tools; no separate MCP action endpoint is required.
Native Decisions preserves individual refusals or unavailable speculative heads without inventing answers: the shared executor rejects a refusal if it is required for the chosen operation, goal, applicable start or chosen target visibility. An unused refused head does not cancel another independently verified operation. Native choice arrays require at least two entries; a head with one real option includes an explicit no-applicable-choice abstention. This reserved value cannot dispatch and is retained in the raw API output; if chosen, that head is treated as unavailable.
For TYPE_TEXT, Navigation can reuse a uniquely matched focused and enabled editable field from the fresh hierarchy. It then skips the focus click and model visibility approval. Identity when available, role, label and bounds must match. Focus is rechecked before text mutations, and the resulting field value must exactly match the supplied reference. This does not establish visual visibility. The optional read-only observation field focusClickDispatched is false when existing focus was reused and true when a focus click was dispatched. Older runs omit it. Reading it in Studio or CLI Script steps and Advanced configuration is synchronous and has no side effects. The viewer does not draw a click marker when this field is false.
Browser Navigation treats a native Date control as one editable target and omits its internal segment candidates. Supply dates as YYYY-MM-DD strings in inputReferences. The asynchronous runner clicks the native English Year, Month and Day segments locally, types the supplied parts and verifies the complete ISO date before continuing. It checks fresh target, segment bounds and focus; invalid dates, unsupported segments, disabled controls or value mismatches fail. This browser-only path does not add Android/iOS date-picker support. Segment clicks are internal to one model TYPE_TEXT action; the existing action budget counts that action once. No DOM value mutation or Browser Host deployment is required.
Browser Navigation offers actionable controls when at least 50% of their rectangle area lies inside the normalized viewport. Other nodes retain full-rectangle filtering. A partially clipped candidate retains its original bounds and exposes clickPoint (normalized x/y), the center of the visible rectangle. Dispatch and the report marker use that point. This geometric fraction is not proof against overlays: the screenshot visibility check must identify an exposed click point, and fresh reachability/keyboard checks remain. A host hit-test for the original center is not reused as positive proof for a different point. Native target filtering is unchanged. CLICK choices also explicitly describe selected expected values that already match editable fields; unrelated current field contents are not sent.
New stale Navigation observations include optional hierarchyChanges with comparison=“hierarchy_paths”, totalChanges and a changes array. Each entry identifies a JSON-pointer path, change type and available before/after values from the original request hierarchy and the fresh dispatch-check hierarchy. This is a path-based structural comparison, not inferred element identity or a causal diagnosis. Editable contents and value properties are redacted; their changes remain recorded. It is stored locally, is not added to model input, and is synchronously readable without side effects through executionEvidence.navigationObservation in Script steps, Advanced configuration and existing MCP run/result tools. Older reports omit the field and the viewer explicitly says the comparison was not recorded. The existing stale detection and no-dispatch behavior are unchanged.