@e2e-dev/decision 0.2.0 → 0.2.1-nightly-20261010091034

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.
package/dist/.build.json CHANGED
@@ -1 +1 @@
1
- {"version":"0.2.0"}
1
+ {"version":"0.2.1-nightly-20261010091034"}
package/dist/dispatch.js CHANGED
@@ -15,6 +15,9 @@ export async function perform(ctx, action) {
15
15
  case 'back':
16
16
  await actions.back();
17
17
  return undefined;
18
+ case 'dismiss_keyboard':
19
+ await actions.dismissKeyboard();
20
+ return undefined;
18
21
  case 'tap':
19
22
  await actions.tap(node(action.target));
20
23
  return undefined;
@@ -1,8 +1,11 @@
1
1
  import type { ExecutorNode, ExecutorPixels, StepExecutorContext } from 'e2e';
2
2
  /** Operations the executor offers on elements. */
3
3
  export type Operation = 'tap' | 'type' | 'typeSecret' | 'submit' | 'select' | 'check' | 'hover' | 'secondary_tap' | 'double_tap' | 'long_press' | 'drag' | 'scroll_to' | 'upload' | 'tap_at';
4
- /** Viewport and history controls, always offered when the engine declares the verb. */
5
- export type Control = 'scroll_up' | 'scroll_down' | 'back';
4
+ /**
5
+ * Viewport, history, and keyboard controls, offered when the engine declares
6
+ * the verb; `dismiss_keyboard` only while an on-screen keyboard is showing.
7
+ */
8
+ export type Control = 'scroll_up' | 'scroll_down' | 'back' | 'dismiss_keyboard';
6
9
  /** Terminal choices: the step claims completion, reports a product failure, or gives up. */
7
10
  export type Terminal = 'done' | 'failed' | 'blocked';
8
11
  /** One row of the element table the decision model reads. */
package/dist/elements.js CHANGED
@@ -14,6 +14,8 @@ const tappable = new Set([
14
14
  const typable = new Set(['textbox', 'searchbox', 'spinbutton', 'combobox']);
15
15
  const secretTypable = new Set(['textbox', 'searchbox', 'combobox']);
16
16
  const checkable = new Set(['checkbox', 'radio', 'switch']);
17
+ /** Roles a device tree gives the soft keyboard and its keys; an iOS number pad lists only its keys. */
18
+ const keyboardRoles = new Set(['keyboard', 'key']);
17
19
  /** Live regions whose text reports what the app just did; a log can run long, so each line and their count are bounded. */
18
20
  const liveRegions = new Set(['status', 'alert', 'log']);
19
21
  const MAX_STATUSES = 20;
@@ -204,6 +206,8 @@ export function actionSpace(ctx, observation, canType) {
204
206
  }
205
207
  if (verbs.has('back'))
206
208
  controls.add('back');
209
+ if (verbs.has('dismissKeyboard') && keyboardShowing(observation.tree))
210
+ controls.add('dismiss_keyboard');
207
211
  return {
208
212
  elements,
209
213
  element: (key) => byIndex.get(targetKeyIndex(key)),
@@ -251,6 +255,17 @@ export function targetKeyIndex(key) {
251
255
  const at = key.indexOf(':');
252
256
  return at === -1 ? key : key.slice(0, at);
253
257
  }
258
+ /**
259
+ * Whether the tree lists an on-screen keyboard. Offering the control only
260
+ * then keeps a choice that does nothing out of the operation question.
261
+ */
262
+ function keyboardShowing(node) {
263
+ if (node.states?.hidden === true)
264
+ return false;
265
+ if (keyboardRoles.has(node.role ?? ''))
266
+ return true;
267
+ return (node.children ?? []).some(keyboardShowing);
268
+ }
254
269
  /** Label the model reads: name, placeholder, or text. */
255
270
  function nodeLabel(node) {
256
271
  return node.name ?? node.attributes?.['placeholder'] ?? node.text ?? '';
package/dist/executor.js CHANGED
@@ -211,7 +211,7 @@ class Step {
211
211
  const acting = { ...view, space: view.space };
212
212
  this.settle(acting);
213
213
  if (stalled(this.history))
214
- return blocked('Three actions in a row changed nothing on screen.');
214
+ return blocked('Three actions in a row failed or changed nothing on screen.');
215
215
  const decided = await this.ask(operationRequest(this.ctx, acting.space, this.history, acting.path));
216
216
  if (decided === undefined)
217
217
  return blocked(this.budgetMessage());
@@ -406,6 +406,7 @@ const CONTROL_LABELS = {
406
406
  scroll_up: 'scroll viewport up',
407
407
  scroll_down: 'scroll viewport down',
408
408
  back: 'back one step in history',
409
+ dismiss_keyboard: 'dismiss the keyboard',
409
410
  };
410
411
  /** The field a text ask describes: the element's label, role, and value; a textbox named by its key when the table has no row. */
411
412
  function fieldOf(key, element) {
@@ -443,11 +444,15 @@ function seedHistory(ctx) {
443
444
  }
444
445
  return seeded;
445
446
  }
446
- /** Three recorded actions in a row with no page change. */
447
+ /**
448
+ * Three recorded actions in a row that failed or left the page unchanged. A
449
+ * failed action is no progress even when the page changed on its own, as a
450
+ * ticking timer does.
451
+ */
447
452
  function stalled(history) {
448
453
  if (history.length < 3)
449
454
  return false;
450
- return history.slice(-3).every((entry) => entry.pageChanged === false);
455
+ return history.slice(-3).every((entry) => entry.error !== undefined || entry.pageChanged === false);
451
456
  }
452
457
  /** No nodes to build an action space from. A truncated observation still acts on what is there. */
453
458
  function emptyTree(observation) {
package/dist/questions.js CHANGED
@@ -49,6 +49,7 @@ const OPERATIONS = {
49
49
  scroll_up: 'Scroll the viewport up.',
50
50
  scroll_down: 'Scroll the viewport down.',
51
51
  back: 'Go back to the previous page, as the browser back button does.',
52
+ dismiss_keyboard: 'The on-screen keyboard is up. Controls it covers are missing from the elements; hide it to reach them.',
52
53
  };
53
54
  /** The fallback criterion of a target question. */
54
55
  const NONE_TARGET = 'No offered target fits the goal.';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e2e-dev/decision",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-nightly-20261010091034",
4
4
  "description": "Decision-model executors for e2e",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -30,18 +30,18 @@
30
30
  },
31
31
  "peerDependencies": {
32
32
  "ai": "^7.0.134",
33
- "e2e": ">=0.19.0 <1"
33
+ "e2e": "0.19.1-nightly-20261010091034"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@ai-sdk/openai": "4.0.90",
37
37
  "@ai-sdk/typesafe-ai": "3.0.16",
38
- "@e2e-dev/web": "0.14.0",
38
+ "@e2e-dev/web": "0.14.1-nightly-20261010091034",
39
39
  "@types/node": "26.6.2",
40
40
  "@types/pngjs": "6.0.5",
41
41
  "ai": "7.0.134",
42
42
  "ajv": "8.20.0",
43
43
  "ajv-formats": "3.0.1",
44
- "e2e": "0.19.0",
44
+ "e2e": "0.19.1-nightly-20261010091034",
45
45
  "typescript": "7.0.2",
46
46
  "vitest": "5.0.1"
47
47
  },