changedInput schema / $defs / PhoneStep / oneOf
Previous value: -[
- {
- "additionalProperties": false,
- "description": "Tap normalized screen coordinates. Prefer `tap_locator` (durable in a\nsaved flow) whenever the target has a label or identifier.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "kind": {
- "const": "tap",
- "type": "string"
- },
- "x": {
- "type": "number"
- },
- "y": {
- "type": "number"
- }
- },
- "required": [
- "kind",
- "x",
- "y"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Long-press normalized screen coordinates.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "duration_ms": {
- "default": 600,
- "type": "integer"
- },
- "kind": {
- "const": "longpress",
- "type": "string"
- },
- "x": {
- "type": "number"
- },
- "y": {
- "type": "number"
- }
- },
- "required": [
- "kind",
- "x",
- "y"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Hold 2–5 distinct normalized points simultaneously; array length is finger count.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "duration_ms": {
- "default": 1000,
- "type": "integer"
- },
- "kind": {
- "const": "multi_longpress",
- "type": "string"
- },
- "points": {
- "items": {
- "$ref": "#/$defs/PhoneHoldPoint"
- },
- "type": "array"
- }
- },
- "required": [
- "kind",
- "points"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Swipe once from one normalized screen point to another.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "duration_ms": {
- "default": 300,
- "type": "integer"
- },
- "kind": {
- "const": "swipe",
- "type": "string"
- },
- "x1": {
- "type": "number"
- },
- "x2": {
- "type": "number"
- },
- "y1": {
- "type": "number"
- },
- "y2": {
- "type": "number"
- }
- },
- "required": [
- "kind",
- "x1",
- "y1",
- "x2",
- "y2"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Hold, then drag continuously. Use either x1/y1/x2/y2 or 2–16\nnormalized points with optional dwell_ms at each stop. For points,\nduration_ms is travel time per segment; total hold/travel/dwell <= 10s.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "duration_ms": {
- "default": 300,
- "type": "integer"
- },
- "hold_ms": {
- "default": 500,
- "type": "integer"
- },
- "kind": {
- "const": "drag",
- "type": "string"
- },
- "points": {
- "default": null,
- "items": {
- "$ref": "#/$defs/PhoneDragPoint"
- },
- "type": [
- "array",
- "null"
- ]
- },
- "x1": {
- "type": [
- "number",
- "null"
- ]
- },
- "x2": {
- "type": [
- "number",
- "null"
- ]
- },
- "y1": {
- "type": [
- "number",
- "null"
- ]
- },
- "y2": {
- "type": [
- "number",
- "null"
- ]
- }
- },
- "required": [
- "kind"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Tap one exact, unique accessibility label.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "kind": {
- "const": "tap_label",
- "type": "string"
- },
- "label": {
- "type": "string"
- }
- },
- "required": [
- "kind",
- "label"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Tap the one current element matching every supplied semantic locator\nfield. Zero or multiple matches send no tap.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "if_present": {
- "description": "Pass instead of failing when no element matches — for screens that\nappear only sometimes (\"Not now\", \"Skip\"). Nothing is sent then.",
- "type": "boolean"
- },
- "kind": {
- "const": "tap_locator",
- "type": "string"
- },
- "locator": {
- "$ref": "#/$defs/PhoneElementLocator"
- },
- "via": {
- "description": "`point`: tap the centre of the element's live frame instead of\nXCUIElement's click — for custom controls that ACK a click and do\nnothing. Default `element`.",
- "type": [
- "string",
- "null"
- ]
- }
- },
- "required": [
- "kind",
- "locator"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Type Unicode text into the focused field. `clear=true` clears that field\nimmediately before inserting the text as one compound action.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "clear": {
- "default": false,
- "type": "boolean"
- },
- "kind": {
- "const": "type",
- "type": "string"
- },
- "text": {
- "type": "string"
- }
- },
- "required": [
- "kind",
- "text"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Send one supported device-native key.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "kind": {
- "const": "key",
- "type": "string"
- },
- "name": {
- "type": "string"
- }
- },
- "required": [
- "kind",
- "name"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Trigger Home or Spotlight.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "kind": {
- "const": "shortcut",
- "type": "string"
- },
- "name": {
- "type": "string"
- }
- },
- "required": [
- "kind",
- "name"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Scroll with the same normalized anchor and deltas as `phone_scroll`.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "dx": {
- "type": "number"
- },
- "dy": {
- "type": "number"
- },
- "kind": {
- "const": "scroll",
- "type": "string"
- },
- "x": {
- "type": "number"
- },
- "y": {
- "type": "number"
- }
- },
- "required": [
- "kind",
- "x",
- "y",
- "dx",
- "dy"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Launch or foreground an installed app: give exactly one of `bundle`\n(its exact bundle identifier) or `app` (its name — 微信, WeChat,\n招商银行, Settings — which the daemon resolves through phone_apps; a\nloose name that starts a word of exactly one installed app's name also\nlaunches it, and the step's `resolved` says which; an ambiguous or\nunknown name is refused with candidates, nothing sent).",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "app": {
- "type": [
- "string",
- "null"
- ]
- },
- "bundle": {
- "type": [
- "string",
- "null"
- ]
- },
- "fresh": {
- "description": "Quit the app first so it opens on its first screen, not the page\nit was left on (flows recorded on the phone start there).",
- "type": "boolean"
- },
- "kind": {
- "const": "launch_app",
- "type": "string"
- }
- },
- "required": [
- "kind"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Open a link (`https://…`, or a deep link such as `weixin://`) with\nthe app iOS assigns to it. http/https always work; other schemes\n(tel, sms, mailto, app deep links) are refused unless the operator\nallowed them in IPHONE_USE_ALLOW_URL_SCHEMES. It leaves the current\napp, so guard the next screen with `wait_for`.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "kind": {
- "const": "open_url",
- "type": "string"
- },
- "url": {
- "type": "string"
- }
- },
- "required": [
- "kind",
- "url"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Navigate back inside the current application.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "kind": {
- "const": "back",
- "type": "string"
- }
- },
- "required": [
- "kind"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Press a button on a system alert (UIAlertController) through the device runner's\nnative alert route — the only path that actually acts on one; taps on\nalert buttons are acknowledged without effect. Give the exact button\ntext, or `action: accept|dismiss` for the default/cancel button. Fails\nclosed when no alert is showing.",
- "properties": {
- "action": {
- "default": null,
- "type": [
- "string",
- "null"
- ]
- },
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "button": {
- "default": null,
- "type": [
- "string",
- "null"
- ]
- },
- "if_present": {
- "description": "Pass instead of failing when no alert is up — for prompts that\nappear only sometimes (\"Allow Paste\"). Flows using it need a\nclient that knows it.",
- "type": "boolean"
- },
- "kind": {
- "const": "alert",
- "type": "string"
- }
- },
- "required": [
- "kind"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Select a value in a native picker wheel.",
- "properties": {
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "column": {
- "default": 0,
- "type": "integer"
- },
- "kind": {
- "const": "picker",
- "type": "string"
- },
- "value": {
- "type": "string"
- }
- },
- "required": [
- "kind",
- "value"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Invoke a named affordance on a snapshot-bound element: toggle a\nswitch, increment/decrement or adjust a slider or stepper, and the\nother names in phone_capabilities (`perform_actions`). `element` and\n`snapshot` come from phone_elements. Snapshot-bound, so not for saved\nflows. `value` only with `adjust`; `duration_ms` only with\n`menu`/`force_press`. Verify the new `value` with wait_for.",
- "properties": {
- "action": {
- "type": "string"
- },
- "after_ms": {
- "default": 0,
- "type": "integer"
- },
- "duration_ms": {
- "default": null,
- "type": [
- "integer",
- "null"
- ]
- },
- "element": {
- "type": "integer"
- },
- "kind": {
- "const": "perform",
- "type": "string"
- },
- "snapshot": {
- "type": "string"
- },
- "value": {
- "default": null,
- "type": [
- "string",
- "null"
- ]
- }
- },
- "required": [
- "kind",
- "element",
- "snapshot",
- "action"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "Poll the current element tree until the semantic expectation holds.",
- "properties": {
- "expect": {
- "$ref": "#/$defs/PhoneUiExpectation"
- },
- "kind": {
- "const": "wait_for",
- "type": "string"
- },
- "poll_ms": {
- "default": 250,
- "type": "integer"
- },
- "timeout_ms": {
- "default": 15000,
- "type": "integer"
- }
- },
- "required": [
- "kind",
- "expect"
- ],
- "type": "object"
- },
- {
- "additionalProperties": false,
- "description": "A short animation pause. Prefer `wait_for` for correctness.",
- "properties": {
- "kind": {
- "const": "pause",
- "type": "string"
- },
- "ms": {
- "type": "integer"
- }
- },
- "required": [
- "kind",
- "ms"
- ],
- "type": "object"
- }
-]New value: +[
+ {
+ "additionalProperties": false,
+ "description": "Tap normalized screen coordinates. Prefer `tap_locator` (durable in a\nsaved flow) whenever the target has a label or identifier.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "tap",
+ "type": "string"
+ },
+ "x": {
+ "type": "number"
+ },
+ "y": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "kind",
+ "x",
+ "y"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Long-press normalized screen coordinates.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "duration_ms": {
+ "default": 600,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "longpress",
+ "type": "string"
+ },
+ "x": {
+ "type": "number"
+ },
+ "y": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "kind",
+ "x",
+ "y"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Hold 2–5 distinct normalized points simultaneously; array length is finger count.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "duration_ms": {
+ "default": 1000,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "multi_longpress",
+ "type": "string"
+ },
+ "points": {
+ "items": {
+ "$ref": "#/$defs/PhoneHoldPoint"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "kind",
+ "points"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Swipe once from one normalized screen point to another.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "duration_ms": {
+ "default": 300,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "swipe",
+ "type": "string"
+ },
+ "x1": {
+ "type": "number"
+ },
+ "x2": {
+ "type": "number"
+ },
+ "y1": {
+ "type": "number"
+ },
+ "y2": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "kind",
+ "x1",
+ "y1",
+ "x2",
+ "y2"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Hold, then drag continuously. Use either x1/y1/x2/y2 or 2–16\nnormalized points with optional dwell_ms at each stop. For points,\nduration_ms is travel time per segment; total hold/travel/dwell <= 10s.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "duration_ms": {
+ "default": 300,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "hold_ms": {
+ "default": 500,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "drag",
+ "type": "string"
+ },
+ "points": {
+ "default": null,
+ "items": {
+ "$ref": "#/$defs/PhoneDragPoint"
+ },
+ "type": [
+ "array",
+ "null"
+ ]
+ },
+ "x1": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "x2": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "y1": {
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "y2": {
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "kind"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Tap one exact, unique accessibility label.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "tap_label",
+ "type": "string"
+ },
+ "label": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "label"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Tap the one current element matching every supplied semantic locator\nfield. Zero or multiple matches send no tap.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "if_present": {
+ "description": "Pass instead of failing when no element matches — for screens that\nappear only sometimes (\"Not now\", \"Skip\"). Nothing is sent then.",
+ "type": "boolean"
+ },
+ "kind": {
+ "const": "tap_locator",
+ "type": "string"
+ },
+ "locator": {
+ "$ref": "#/$defs/PhoneElementLocator"
+ },
+ "via": {
+ "description": "`point`: tap the centre of the element's live frame instead of\nXCUIElement's click — for custom controls that ACK a click and do\nnothing. Default `element`.",
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "kind",
+ "locator"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Type Unicode text into the focused field. `clear=true` clears that field\nimmediately before inserting the text as one compound action.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "budget_ms": {
+ "description": "Optional per-call typing budget (6000..15000 ms).",
+ "maximum": 15000,
+ "minimum": 6000,
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "clear": {
+ "default": false,
+ "type": "boolean"
+ },
+ "kind": {
+ "const": "type",
+ "type": "string"
+ },
+ "text": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "text"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Continue only the unsent suffix held under a single-use original-field token.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "continue_token": {
+ "maxLength": 64,
+ "minLength": 64,
+ "pattern": "^[0-9a-f]{64}$",
+ "type": "string"
+ },
+ "kind": {
+ "const": "type_continue",
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "continue_token"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Send one supported device-native key.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "key",
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "name"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Trigger Home or Spotlight.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "shortcut",
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "name"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Scroll with the same normalized anchor and deltas as `phone_scroll`.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "dx": {
+ "type": "number"
+ },
+ "dy": {
+ "type": "number"
+ },
+ "kind": {
+ "const": "scroll",
+ "type": "string"
+ },
+ "x": {
+ "type": "number"
+ },
+ "y": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "kind",
+ "x",
+ "y",
+ "dx",
+ "dy"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Launch or foreground an installed app: give exactly one of `bundle`\n(its exact bundle identifier) or `app` (its name — 微信, WeChat,\n招商银行, Settings — which the daemon resolves through phone_apps; a\nloose name that starts a word of exactly one installed app's name also\nlaunches it, and the step's `resolved` says which; an ambiguous or\nunknown name is refused with candidates, nothing sent).",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "app": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "bundle": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "fresh": {
+ "description": "Quit the app first so it opens on its first screen, not the page\nit was left on (flows recorded on the phone start there).",
+ "type": "boolean"
+ },
+ "kind": {
+ "const": "launch_app",
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Open a link (`https://…`, or a deep link such as `weixin://`) with\nthe app iOS assigns to it. http/https always work; other schemes\n(tel, sms, mailto, app deep links) are refused unless the operator\nallowed them in IPHONE_USE_ALLOW_URL_SCHEMES. It leaves the current\napp, so guard the next screen with `wait_for`.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "open_url",
+ "type": "string"
+ },
+ "url": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "url"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Navigate back inside the current application.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "back",
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Press a button on a system alert (UIAlertController) through the device runner's\nnative alert route — the only path that actually acts on one; taps on\nalert buttons are acknowledged without effect. Give the exact button\ntext, or `action: accept|dismiss` for the default/cancel button. Fails\nclosed when no alert is showing.",
+ "properties": {
+ "action": {
+ "default": null,
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "button": {
+ "default": null,
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "if_present": {
+ "description": "Pass instead of failing when no alert is up — for prompts that\nappear only sometimes (\"Allow Paste\"). Flows using it need a\nclient that knows it.",
+ "type": "boolean"
+ },
+ "kind": {
+ "const": "alert",
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Select a value in a native picker wheel.",
+ "properties": {
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "column": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "picker",
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "kind",
+ "value"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Invoke a named affordance on a snapshot-bound element: toggle a\nswitch, increment/decrement or adjust a slider or stepper, and the\nother names in phone_capabilities (`perform_actions`). `element` and\n`snapshot` come from phone_elements. Snapshot-bound, so not for saved\nflows. `value` only with `adjust`; `duration_ms` only with\n`menu`/`force_press`. Verify the new `value` with wait_for.",
+ "properties": {
+ "action": {
+ "type": "string"
+ },
+ "after_ms": {
+ "default": 0,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "duration_ms": {
+ "default": null,
+ "minimum": 0,
+ "type": [
+ "integer",
+ "null"
+ ]
+ },
+ "element": {
+ "minimum": 0,
+ "type": "integer"
+ },
+ "kind": {
+ "const": "perform",
+ "type": "string"
+ },
+ "snapshot": {
+ "type": "string"
+ },
+ "value": {
+ "default": null,
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ },
+ "required": [
+ "kind",
+ "element",
+ "snapshot",
+ "action"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "Poll until the expectation holds. For tree waits, timeout_ms:0 makes\none observation attempt bounded to 500ms, without polling or read retry.\nUse observe:false to omit the separate final batch observation.",
+ "properties": {
+ "expect": {
+ "$ref": "#/$defs/PhoneUiExpectation"
+ },
+ "kind": {
+ "const": "wait_for",
+ "type": "string"
+ },
+ "poll_ms": {
+ "default": 250,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "timeout_ms": {
+ "default": 15000,
+ "minimum": 0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "kind",
+ "expect"
+ ],
+ "type": "object"
+ },
+ {
+ "additionalProperties": false,
+ "description": "A short animation pause. Prefer `wait_for` for correctness.",
+ "properties": {
+ "kind": {
+ "const": "pause",
+ "type": "string"
+ },
+ "ms": {
+ "minimum": 0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "kind",
+ "ms"
+ ],
+ "type": "object"
+ }
+]