@riddledc/riddle-proof 0.7.180 → 0.7.182

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/README.md CHANGED
@@ -402,7 +402,7 @@ when body matching overrides sequence order.
402
402
  `target.setup_actions` is optional. Use it when the meaningful proof surface
403
403
  appears only after a picker, tab, login stub, storage seed, form fill,
404
404
  transport control, or other bounded interaction. Supported setup actions are
405
- `click`, `tap`, `drag`, `press`, `fill`, `set_input_value`, `set_range_value`,
405
+ `click`, `tap`, `drag`, `press`, `key_down`, `key_up`, `fill`, `set_input_value`, `set_range_value`,
406
406
  `deterministic_runtime`, `canvas_signature`, `assert_text_visible`, `assert_text_absent`,
407
407
  `assert_selector_count`, `assert_window_value`, `assert_window_number`,
408
408
  `local_storage`, `session_storage`, `clear_storage`, `clear_console`,
@@ -417,6 +417,11 @@ stable enough for Playwright's default click actionability checks. Use `press`
417
417
  with a Playwright key name, such as `Enter`, `Space`, or `ArrowLeft`,
418
418
  when a route's intended browser control is keyboard-driven; omit `selector` for
419
419
  a page-level key press, or provide `selector` to press against a focused element.
420
+ For canvas games that read key state rather than keypress events, add `hold_ms`
421
+ or `holdMs` to keep the key down before releasing it. When the profile needs
422
+ to observe or wait on runtime evidence while a key remains held, use paired
423
+ `key_down` / `key_up` actions around `wait`, `window_eval`, or
424
+ `window_call_until` actions.
420
425
  Use `click_count` / `clickCount` / `clicks` from 1 to 10 on a single `click`
421
426
  action for atomic double-click or double-submit contracts where modeling the
422
427
  interaction as repeated setup actions would incorrectly require the target to
@@ -47,6 +47,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
47
47
  "tap",
48
48
  "drag",
49
49
  "press",
50
+ "key_down",
51
+ "key_up",
50
52
  "fill",
51
53
  "set_input_value",
52
54
  "set_range_value",
@@ -612,12 +614,14 @@ function profileSetupTapReceipts(results) {
612
614
  }));
613
615
  }
614
616
  function profileSetupPressReceipts(results) {
615
- return results.filter((result) => profileSetupResultAction(result) === "press").map((result) => ({
617
+ return results.filter((result) => ["press", "key_down", "key_up"].includes(profileSetupResultAction(result))).map((result) => ({
618
+ action: profileSetupResultAction(result),
616
619
  ordinal: result.ordinal ?? null,
617
620
  ok: result.ok !== false,
618
621
  selector: result.selector ?? null,
619
622
  frame_selector: result.frame_selector ?? null,
620
623
  key: result.key ?? null,
624
+ hold_ms: result.hold_ms ?? null,
621
625
  reason: result.reason ?? result.error ?? null
622
626
  }));
623
627
  }
@@ -912,7 +916,7 @@ function isSupportedCheckType(value) {
912
916
  }
913
917
  function normalizeSetupActionType(value, index) {
914
918
  const normalizedInput = String(value || "").trim().replace(/-/g, "_");
915
- const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
919
+ const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "keyboard_down" || normalizedInput === "key_down" || normalizedInput === "keydown" || normalizedInput === "press_down" ? "key_down" : normalizedInput === "keyboard_up" || normalizedInput === "key_up" || normalizedInput === "keyup" || normalizedInput === "press_up" || normalizedInput === "release_key" || normalizedInput === "key_release" ? "key_up" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
916
920
  if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
917
921
  return normalized;
918
922
  }
@@ -1083,6 +1087,11 @@ function normalizeSetupAction(input, index) {
1083
1087
  const toY = numberValue(valueFromOwn(input, "to_y", "toY", "end_y", "endY", "y2"));
1084
1088
  const coordinateMode = normalizeSetupActionCoordinateMode(valueFromOwn(input, "coordinate_mode", "coordinateMode", "coords", "units"), index);
1085
1089
  const pointerType = normalizeSetupActionPointerType(valueFromOwn(input, "pointer_type", "pointerType", "input_type", "inputType"), type, index);
1090
+ const durationMs = numberValue(input.duration_ms) ?? numberValue(input.durationMs);
1091
+ const holdMs = type === "press" ? normalizeSetupActionNonNegativeNumber(input, index, "hold_ms", "hold_ms", "holdMs", "key_down_ms", "keyDownMs", "down_ms", "downMs") ?? durationMs : void 0;
1092
+ if (type === "press" && holdMs !== void 0 && (holdMs < 0 || holdMs > 3e4)) {
1093
+ throw new Error(`target.setup_actions[${index}].hold_ms must be a finite number from 0 to 30000.`);
1094
+ }
1086
1095
  if (type === "click") {
1087
1096
  const hasClickCoordinate = fromX !== void 0 || fromY !== void 0;
1088
1097
  if (hasClickCoordinate && (fromX === void 0 || fromY === void 0)) {
@@ -1163,7 +1172,7 @@ function normalizeSetupAction(input, index) {
1163
1172
  dialogAccept = true;
1164
1173
  }
1165
1174
  }
1166
- if (type === "press" && !key) {
1175
+ if ((type === "press" || type === "key_down" || type === "key_up") && !key) {
1167
1176
  throw new Error(`target.setup_actions[${index}] ${type} requires key.`);
1168
1177
  }
1169
1178
  if ((type === "local_storage" || type === "session_storage") && !key) {
@@ -1249,9 +1258,10 @@ function normalizeSetupAction(input, index) {
1249
1258
  from_y: fromY,
1250
1259
  to_x: toX,
1251
1260
  to_y: toY,
1252
- duration_ms: numberValue(input.duration_ms) ?? numberValue(input.durationMs),
1261
+ duration_ms: durationMs,
1253
1262
  steps,
1254
1263
  key,
1264
+ hold_ms: holdMs,
1255
1265
  value,
1256
1266
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
1257
1267
  random_queue: randomQueue,
@@ -4489,13 +4499,15 @@ function profileSetupTapReceipts(results) {
4489
4499
  }
4490
4500
  function profileSetupPressReceipts(results) {
4491
4501
  return (results || [])
4492
- .filter((result) => result && profileSetupResultAction(result) === "press")
4502
+ .filter((result) => result && ["press", "key_down", "key_up"].includes(profileSetupResultAction(result)))
4493
4503
  .map((result) => ({
4504
+ action: profileSetupResultAction(result),
4494
4505
  ordinal: result.ordinal ?? null,
4495
4506
  ok: result.ok !== false,
4496
4507
  selector: result.selector ?? null,
4497
4508
  frame_selector: result.frame_selector ?? null,
4498
4509
  key: result.key ?? null,
4510
+ hold_ms: result.hold_ms ?? null,
4499
4511
  reason: result.reason || result.error || null,
4500
4512
  }));
4501
4513
  }
@@ -6429,12 +6441,56 @@ async function executeSetupAction(action, ordinal, viewport) {
6429
6441
  duration_ms: durationMs || undefined,
6430
6442
  };
6431
6443
  }
6444
+ if (type === "key_down" || type === "key_up") {
6445
+ const key = String(action.key || "").trim();
6446
+ if (!key) return { ...base, reason: "missing_key" };
6447
+ const scope = await setupActionScope(action, timeout);
6448
+ if (!scope.ok) return setupScopeFailure(base, scope);
6449
+ let count;
6450
+ let targetIndex;
6451
+ if (action.selector) {
6452
+ const locator = scope.context.locator(action.selector);
6453
+ count = await locator.count();
6454
+ if (!count) return { ...base, reason: "selector_not_found", count, key };
6455
+ targetIndex = Number.isInteger(action.index) ? action.index : 0;
6456
+ if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
6457
+ await locator.nth(targetIndex).focus({ timeout }).catch(() => {});
6458
+ } else if (scope.frame_selector) {
6459
+ await scope.context.locator("body").focus({ timeout }).catch(() => {});
6460
+ }
6461
+ if (type === "key_down") {
6462
+ await page.keyboard.down(key);
6463
+ } else {
6464
+ await page.keyboard.up(key);
6465
+ }
6466
+ return {
6467
+ ...base,
6468
+ ...setupScopeEvidence(scope),
6469
+ ok: true,
6470
+ count,
6471
+ target_index: targetIndex,
6472
+ key,
6473
+ };
6474
+ }
6432
6475
  if (type === "press") {
6433
6476
  const key = String(action.key || "").trim();
6434
6477
  if (!key) return { ...base, reason: "missing_key" };
6478
+ const holdMs = Math.min(30000, Math.max(0, Math.floor(setupNumber(action.hold_ms ?? action.holdMs ?? action.key_down_ms ?? action.keyDownMs ?? action.down_ms ?? action.downMs ?? action.duration_ms ?? action.durationMs, 0) || 0)));
6435
6479
  const scope = await setupActionScope(action, timeout);
6436
6480
  if (!scope.ok) return setupScopeFailure(base, scope);
6437
6481
  if (!action.selector) {
6482
+ if (holdMs > 0) {
6483
+ if (scope.frame_selector) {
6484
+ await scope.context.locator("body").focus({ timeout }).catch(() => {});
6485
+ }
6486
+ await page.keyboard.down(key);
6487
+ try {
6488
+ await page.waitForTimeout(holdMs);
6489
+ } finally {
6490
+ await page.keyboard.up(key).catch(() => {});
6491
+ }
6492
+ return { ...base, ...setupScopeEvidence(scope), ok: true, key, hold_ms: holdMs };
6493
+ }
6438
6494
  if (scope.frame_selector) {
6439
6495
  await scope.context.locator("body").press(key, { timeout });
6440
6496
  } else {
@@ -6447,6 +6503,16 @@ async function executeSetupAction(action, ordinal, viewport) {
6447
6503
  if (!count) return { ...base, reason: "selector_not_found", count, key };
6448
6504
  const targetIndex = Number.isInteger(action.index) ? action.index : 0;
6449
6505
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
6506
+ if (holdMs > 0) {
6507
+ await locator.nth(targetIndex).focus({ timeout });
6508
+ await page.keyboard.down(key);
6509
+ try {
6510
+ await page.waitForTimeout(holdMs);
6511
+ } finally {
6512
+ await page.keyboard.up(key).catch(() => {});
6513
+ }
6514
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key, hold_ms: holdMs };
6515
+ }
6450
6516
  await locator.nth(targetIndex).press(key, { timeout });
6451
6517
  return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key };
6452
6518
  }
package/dist/cli.cjs CHANGED
@@ -7004,6 +7004,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
7004
7004
  "tap",
7005
7005
  "drag",
7006
7006
  "press",
7007
+ "key_down",
7008
+ "key_up",
7007
7009
  "fill",
7008
7010
  "set_input_value",
7009
7011
  "set_range_value",
@@ -7569,12 +7571,14 @@ function profileSetupTapReceipts(results) {
7569
7571
  }));
7570
7572
  }
7571
7573
  function profileSetupPressReceipts(results) {
7572
- return results.filter((result) => profileSetupResultAction(result) === "press").map((result) => ({
7574
+ return results.filter((result) => ["press", "key_down", "key_up"].includes(profileSetupResultAction(result))).map((result) => ({
7575
+ action: profileSetupResultAction(result),
7573
7576
  ordinal: result.ordinal ?? null,
7574
7577
  ok: result.ok !== false,
7575
7578
  selector: result.selector ?? null,
7576
7579
  frame_selector: result.frame_selector ?? null,
7577
7580
  key: result.key ?? null,
7581
+ hold_ms: result.hold_ms ?? null,
7578
7582
  reason: result.reason ?? result.error ?? null
7579
7583
  }));
7580
7584
  }
@@ -7869,7 +7873,7 @@ function isSupportedCheckType(value) {
7869
7873
  }
7870
7874
  function normalizeSetupActionType(value, index) {
7871
7875
  const normalizedInput = String(value || "").trim().replace(/-/g, "_");
7872
- const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
7876
+ const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "keyboard_down" || normalizedInput === "key_down" || normalizedInput === "keydown" || normalizedInput === "press_down" ? "key_down" : normalizedInput === "keyboard_up" || normalizedInput === "key_up" || normalizedInput === "keyup" || normalizedInput === "press_up" || normalizedInput === "release_key" || normalizedInput === "key_release" ? "key_up" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
7873
7877
  if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
7874
7878
  return normalized;
7875
7879
  }
@@ -8040,6 +8044,11 @@ function normalizeSetupAction(input, index) {
8040
8044
  const toY = numberValue(valueFromOwn(input, "to_y", "toY", "end_y", "endY", "y2"));
8041
8045
  const coordinateMode = normalizeSetupActionCoordinateMode(valueFromOwn(input, "coordinate_mode", "coordinateMode", "coords", "units"), index);
8042
8046
  const pointerType = normalizeSetupActionPointerType(valueFromOwn(input, "pointer_type", "pointerType", "input_type", "inputType"), type, index);
8047
+ const durationMs = numberValue(input.duration_ms) ?? numberValue(input.durationMs);
8048
+ const holdMs = type === "press" ? normalizeSetupActionNonNegativeNumber(input, index, "hold_ms", "hold_ms", "holdMs", "key_down_ms", "keyDownMs", "down_ms", "downMs") ?? durationMs : void 0;
8049
+ if (type === "press" && holdMs !== void 0 && (holdMs < 0 || holdMs > 3e4)) {
8050
+ throw new Error(`target.setup_actions[${index}].hold_ms must be a finite number from 0 to 30000.`);
8051
+ }
8043
8052
  if (type === "click") {
8044
8053
  const hasClickCoordinate = fromX !== void 0 || fromY !== void 0;
8045
8054
  if (hasClickCoordinate && (fromX === void 0 || fromY === void 0)) {
@@ -8120,7 +8129,7 @@ function normalizeSetupAction(input, index) {
8120
8129
  dialogAccept = true;
8121
8130
  }
8122
8131
  }
8123
- if (type === "press" && !key) {
8132
+ if ((type === "press" || type === "key_down" || type === "key_up") && !key) {
8124
8133
  throw new Error(`target.setup_actions[${index}] ${type} requires key.`);
8125
8134
  }
8126
8135
  if ((type === "local_storage" || type === "session_storage") && !key) {
@@ -8206,9 +8215,10 @@ function normalizeSetupAction(input, index) {
8206
8215
  from_y: fromY,
8207
8216
  to_x: toX,
8208
8217
  to_y: toY,
8209
- duration_ms: numberValue(input.duration_ms) ?? numberValue(input.durationMs),
8218
+ duration_ms: durationMs,
8210
8219
  steps,
8211
8220
  key,
8221
+ hold_ms: holdMs,
8212
8222
  value,
8213
8223
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
8214
8224
  random_queue: randomQueue,
@@ -11430,13 +11440,15 @@ function profileSetupTapReceipts(results) {
11430
11440
  }
11431
11441
  function profileSetupPressReceipts(results) {
11432
11442
  return (results || [])
11433
- .filter((result) => result && profileSetupResultAction(result) === "press")
11443
+ .filter((result) => result && ["press", "key_down", "key_up"].includes(profileSetupResultAction(result)))
11434
11444
  .map((result) => ({
11445
+ action: profileSetupResultAction(result),
11435
11446
  ordinal: result.ordinal ?? null,
11436
11447
  ok: result.ok !== false,
11437
11448
  selector: result.selector ?? null,
11438
11449
  frame_selector: result.frame_selector ?? null,
11439
11450
  key: result.key ?? null,
11451
+ hold_ms: result.hold_ms ?? null,
11440
11452
  reason: result.reason || result.error || null,
11441
11453
  }));
11442
11454
  }
@@ -13370,12 +13382,56 @@ async function executeSetupAction(action, ordinal, viewport) {
13370
13382
  duration_ms: durationMs || undefined,
13371
13383
  };
13372
13384
  }
13385
+ if (type === "key_down" || type === "key_up") {
13386
+ const key = String(action.key || "").trim();
13387
+ if (!key) return { ...base, reason: "missing_key" };
13388
+ const scope = await setupActionScope(action, timeout);
13389
+ if (!scope.ok) return setupScopeFailure(base, scope);
13390
+ let count;
13391
+ let targetIndex;
13392
+ if (action.selector) {
13393
+ const locator = scope.context.locator(action.selector);
13394
+ count = await locator.count();
13395
+ if (!count) return { ...base, reason: "selector_not_found", count, key };
13396
+ targetIndex = Number.isInteger(action.index) ? action.index : 0;
13397
+ if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
13398
+ await locator.nth(targetIndex).focus({ timeout }).catch(() => {});
13399
+ } else if (scope.frame_selector) {
13400
+ await scope.context.locator("body").focus({ timeout }).catch(() => {});
13401
+ }
13402
+ if (type === "key_down") {
13403
+ await page.keyboard.down(key);
13404
+ } else {
13405
+ await page.keyboard.up(key);
13406
+ }
13407
+ return {
13408
+ ...base,
13409
+ ...setupScopeEvidence(scope),
13410
+ ok: true,
13411
+ count,
13412
+ target_index: targetIndex,
13413
+ key,
13414
+ };
13415
+ }
13373
13416
  if (type === "press") {
13374
13417
  const key = String(action.key || "").trim();
13375
13418
  if (!key) return { ...base, reason: "missing_key" };
13419
+ const holdMs = Math.min(30000, Math.max(0, Math.floor(setupNumber(action.hold_ms ?? action.holdMs ?? action.key_down_ms ?? action.keyDownMs ?? action.down_ms ?? action.downMs ?? action.duration_ms ?? action.durationMs, 0) || 0)));
13376
13420
  const scope = await setupActionScope(action, timeout);
13377
13421
  if (!scope.ok) return setupScopeFailure(base, scope);
13378
13422
  if (!action.selector) {
13423
+ if (holdMs > 0) {
13424
+ if (scope.frame_selector) {
13425
+ await scope.context.locator("body").focus({ timeout }).catch(() => {});
13426
+ }
13427
+ await page.keyboard.down(key);
13428
+ try {
13429
+ await page.waitForTimeout(holdMs);
13430
+ } finally {
13431
+ await page.keyboard.up(key).catch(() => {});
13432
+ }
13433
+ return { ...base, ...setupScopeEvidence(scope), ok: true, key, hold_ms: holdMs };
13434
+ }
13379
13435
  if (scope.frame_selector) {
13380
13436
  await scope.context.locator("body").press(key, { timeout });
13381
13437
  } else {
@@ -13388,6 +13444,16 @@ async function executeSetupAction(action, ordinal, viewport) {
13388
13444
  if (!count) return { ...base, reason: "selector_not_found", count, key };
13389
13445
  const targetIndex = Number.isInteger(action.index) ? action.index : 0;
13390
13446
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
13447
+ if (holdMs > 0) {
13448
+ await locator.nth(targetIndex).focus({ timeout });
13449
+ await page.keyboard.down(key);
13450
+ try {
13451
+ await page.waitForTimeout(holdMs);
13452
+ } finally {
13453
+ await page.keyboard.up(key).catch(() => {});
13454
+ }
13455
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key, hold_ms: holdMs };
13456
+ }
13391
13457
  await locator.nth(targetIndex).press(key, { timeout });
13392
13458
  return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key };
13393
13459
  }
@@ -16339,7 +16405,18 @@ function profileCheckMarkdownTarget(check) {
16339
16405
  }
16340
16406
  if (check.type === "no_console_warnings") {
16341
16407
  const warningCount = cliFiniteNumber(evidence.console_warning_count) ?? 0;
16342
- return `${warningCount} unallowed warning${warningCount === 1 ? "" : "s"}`;
16408
+ const totalConsoleCount = cliFiniteNumber(evidence.total_console_warning_count);
16409
+ const allowedConsoleCount = cliFiniteNumber(evidence.allowed_console_warning_count);
16410
+ const allowedTextCount = Array.isArray(evidence.allowed_console_texts) ? evidence.allowed_console_texts.filter((value) => typeof value === "string" && value.trim()).length : 0;
16411
+ const allowedPatternCount = Array.isArray(evidence.allowed_console_patterns) ? evidence.allowed_console_patterns.filter((value) => typeof value === "string" && value.trim()).length : 0;
16412
+ const parts = [`${warningCount} unallowed warning${warningCount === 1 ? "" : "s"}`];
16413
+ if (totalConsoleCount !== void 0 && allowedConsoleCount !== void 0) {
16414
+ parts.push(`${allowedConsoleCount}/${totalConsoleCount} warning${totalConsoleCount === 1 ? "" : "s"} allowed`);
16415
+ }
16416
+ if (allowedTextCount || allowedPatternCount) {
16417
+ parts.push(`allowlist ${allowedTextCount} text${allowedTextCount === 1 ? "" : "s"}, ${allowedPatternCount} pattern${allowedPatternCount === 1 ? "" : "s"}`);
16418
+ }
16419
+ return parts.join(", ");
16343
16420
  }
16344
16421
  return void 0;
16345
16422
  }
@@ -16376,6 +16453,100 @@ function cliReturnSummaryLabel(value) {
16376
16453
  }).filter(Boolean);
16377
16454
  return parts.length ? parts.join(", ") : void 0;
16378
16455
  }
16456
+ function setupFailureObstructionSnippet(reason) {
16457
+ if (!reason || !reason.includes("intercepts pointer events")) return void 0;
16458
+ const line = reason.split(/\r?\n/).find((item) => item.includes("intercepts pointer events"));
16459
+ const match = line?.match(/-\s+(.+?)\s+intercepts pointer events/);
16460
+ const snippet = match?.[1]?.replace(/\s+/g, " ").trim();
16461
+ return snippet || void 0;
16462
+ }
16463
+ function setupReceiptArray(viewport, key) {
16464
+ return Array.isArray(viewport[key]) ? viewport[key].map(cliRecord).filter((item) => Boolean(item)) : [];
16465
+ }
16466
+ function setupReturnSummaryValue(receipt, names) {
16467
+ for (const name of names) {
16468
+ if (receipt[name] !== void 0) return receipt[name];
16469
+ }
16470
+ const returned = cliRecord(receipt.returned);
16471
+ for (const name of names) {
16472
+ if (returned?.[name] !== void 0) return returned[name];
16473
+ }
16474
+ const summaries = Array.isArray(receipt.return_summary) ? receipt.return_summary.map(cliRecord).filter((item) => Boolean(item)) : [];
16475
+ for (const item of summaries) {
16476
+ const label = cliString(item.label);
16477
+ const path7 = cliString(item.path);
16478
+ if (names.some((name) => label === name || path7 === name)) return item.value;
16479
+ }
16480
+ return void 0;
16481
+ }
16482
+ function setupMetricPart(receipts, label, names = [label]) {
16483
+ for (const receipt of receipts) {
16484
+ const value = setupReturnSummaryValue(receipt, names);
16485
+ const valueLabel = cliValueLabel(value);
16486
+ if (valueLabel !== void 0) return `${label}=${valueLabel}`;
16487
+ }
16488
+ return void 0;
16489
+ }
16490
+ function setupInputReceiptLabel(kind, receipt) {
16491
+ const selector = cliString(receipt.selector);
16492
+ const pointerType = cliString(receipt.pointer_type);
16493
+ const inputDispatch = cliString(receipt.input_dispatch);
16494
+ const key = cliString(receipt.key);
16495
+ return [
16496
+ kind,
16497
+ selector ? markdownInlineCode(selector) : "",
16498
+ pointerType ? markdownInlineCode(pointerType) : "",
16499
+ key ? markdownInlineCode(key) : "",
16500
+ inputDispatch ? `via ${markdownInlineCode(inputDispatch)}` : ""
16501
+ ].filter(Boolean).join(" ");
16502
+ }
16503
+ function setupCanvasHashChangeLabel(receipts) {
16504
+ const changed = receipts.find((receipt) => cliString(receipt.previous_hash) && cliString(receipt.hash) && receipt.changed === true);
16505
+ if (changed) return `${markdownInlineCode(cliString(changed.previous_hash) || "")} -> ${markdownInlineCode(cliString(changed.hash) || "")}`;
16506
+ const hashes = receipts.map((receipt) => cliString(receipt.hash)).filter((hash) => Boolean(hash));
16507
+ const first = hashes[0];
16508
+ const last = [...hashes].reverse().find((hash) => hash !== first);
16509
+ return first && last ? `${markdownInlineCode(first)} -> ${markdownInlineCode(last)}` : void 0;
16510
+ }
16511
+ function setupNaturalInputSummaryMarkdown(viewports) {
16512
+ const lines = [];
16513
+ for (const viewport of viewports.slice(0, 8)) {
16514
+ const name = cliString(viewport.name) || "viewport";
16515
+ const inputReceipts = [
16516
+ ...setupReceiptArray(viewport, "drag").map((receipt) => ({ kind: "drag", receipt })),
16517
+ ...setupReceiptArray(viewport, "tap").map((receipt) => ({ kind: "tap", receipt })),
16518
+ ...setupReceiptArray(viewport, "press").map((receipt) => ({ kind: "press", receipt }))
16519
+ ].filter(({ receipt }) => receipt.ok !== false);
16520
+ if (!inputReceipts.length) continue;
16521
+ const valueReceipts = [
16522
+ ...setupReceiptArray(viewport, "window_eval"),
16523
+ ...setupReceiptArray(viewport, "window_call")
16524
+ ].filter((receipt) => receipt.ok !== false);
16525
+ const canvasReceipts = setupReceiptArray(viewport, "canvas_signature").filter((receipt) => receipt.ok !== false);
16526
+ const eventParts = [
16527
+ setupMetricPart(valueReceipts, "pointerDowns"),
16528
+ setupMetricPart(valueReceipts, "pointerMoves"),
16529
+ setupMetricPart(valueReceipts, "pointerUps"),
16530
+ setupMetricPart(valueReceipts, "trustedEvents"),
16531
+ setupMetricPart(valueReceipts, "eventCount")
16532
+ ].filter((part) => Boolean(part));
16533
+ const pixelParts = [
16534
+ setupMetricPart(valueReceipts, "nonWhiteDelta"),
16535
+ setupMetricPart(valueReceipts, "darkDelta")
16536
+ ].filter((part) => Boolean(part));
16537
+ const hashChange = setupCanvasHashChangeLabel(canvasReceipts);
16538
+ if (!eventParts.length && !pixelParts.length && !hashChange) continue;
16539
+ const inputText = inputReceipts.slice(0, 3).map(({ kind, receipt }) => setupInputReceiptLabel(kind, receipt)).join(", ");
16540
+ const parts = [
16541
+ inputText,
16542
+ eventParts.length ? `events ${eventParts.join(", ")}` : "",
16543
+ pixelParts.length ? `pixel deltas ${pixelParts.join(", ")}` : "",
16544
+ hashChange ? `canvas hash ${hashChange}` : ""
16545
+ ].filter(Boolean);
16546
+ lines.push(`- natural input ${name}: ${parts.join("; ")}`);
16547
+ }
16548
+ return lines;
16549
+ }
16379
16550
  function balancedSetupReceiptDetails(groups, limit) {
16380
16551
  if (limit <= 0) return [];
16381
16552
  const total = groups.reduce((sum, group) => sum + group.length, 0);
@@ -16565,6 +16736,7 @@ function profileSetupSummaryMarkdown(result) {
16565
16736
  if (canvasSignatureTotal) {
16566
16737
  lines.push(`- canvas_signature: ${canvasSignatureTotal} action(s)`);
16567
16738
  }
16739
+ lines.push(...setupNaturalInputSummaryMarkdown(viewports));
16568
16740
  for (const viewport of viewports.slice(0, 8)) {
16569
16741
  const name = cliString(viewport.name) || "viewport";
16570
16742
  const ok = viewport.ok === false ? "failed" : "ok";
@@ -16676,12 +16848,14 @@ function profileSetupSummaryMarkdown(result) {
16676
16848
  }
16677
16849
  const sampledPressDetails = balancedSetupReceiptDetails(pressGroups, 12);
16678
16850
  for (const { name, receipt } of sampledPressDetails) {
16851
+ const action = cliString(receipt.action) || "press";
16679
16852
  const key = cliString(receipt.key) || "key";
16680
16853
  const selector = cliString(receipt.selector);
16681
16854
  const frameSelector = cliString(receipt.frame_selector);
16855
+ const holdMs = cliFiniteNumber(receipt.hold_ms);
16682
16856
  const ok = receipt.ok === false ? "failed" : "ok";
16683
16857
  const reason = cliString(receipt.reason);
16684
- lines.push(`- ${name} press: ${ok}, ${markdownInlineCode(key)}${selector ? ` on ${markdownInlineCode(selector)}` : ""}${frameSelector ? ` in frame ${markdownInlineCode(frameSelector)}` : ""}${reason ? `, reason ${markdownInlineCode(reason, 100)}` : ""}`);
16858
+ lines.push(`- ${name} ${action}: ${ok}, ${markdownInlineCode(key)}${selector ? ` on ${markdownInlineCode(selector)}` : ""}${frameSelector ? ` in frame ${markdownInlineCode(frameSelector)}` : ""}${holdMs === void 0 ? "" : `, held ${holdMs}ms`}${reason ? `, reason ${markdownInlineCode(reason, 100)}` : ""}`);
16685
16859
  }
16686
16860
  if (pressDetails.length > sampledPressDetails.length) lines.push(`- ${pressDetails.length - sampledPressDetails.length} additional press receipt(s) omitted.`);
16687
16861
  const canvasSignatureGroups = viewports.map((viewport) => {
@@ -16844,7 +17018,11 @@ function profileSetupSummaryMarkdown(result) {
16844
17018
  const selector = cliString(failure.selector);
16845
17019
  const reason = cliString(failure.reason);
16846
17020
  const caseInsensitiveText = cliString(failure.case_insensitive_text);
17021
+ const obstruction = setupFailureObstructionSnippet(reason);
16847
17022
  lines.push(`- failed ${name}: ${action}${selector ? ` ${markdownInlineCode(selector)}` : ""}${reason ? ` reason ${markdownInlineCode(reason)}` : ""}${caseInsensitiveText ? `; case-insensitive sample ${markdownInlineCode(caseInsensitiveText, 140)}` : ""}`);
17023
+ if (obstruction) {
17024
+ lines.push(`- obstruction ${name}: target ${selector ? markdownInlineCode(selector) : markdownInlineCode(action)} intercepted by ${markdownInlineCode(obstruction, 120)}`);
17025
+ }
16848
17026
  }
16849
17027
  if (failedDetails.length > 8) lines.push(`- ${failedDetails.length - 8} additional failed setup action(s) omitted.`);
16850
17028
  if (viewports.length > 8) lines.push(`- ${viewports.length - 8} additional viewport(s) omitted from setup summary.`);
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  profileStatusExitCode,
14
14
  resolveRiddleProofProfileTargetUrl,
15
15
  resolveRiddleProofProfileTimeoutSec
16
- } from "./chunk-62JD4MJA.js";
16
+ } from "./chunk-XL7Q4M6D.js";
17
17
  import {
18
18
  createRiddleApiClient,
19
19
  isTerminalRiddleJobStatus,
@@ -622,7 +622,18 @@ function profileCheckMarkdownTarget(check) {
622
622
  }
623
623
  if (check.type === "no_console_warnings") {
624
624
  const warningCount = cliFiniteNumber(evidence.console_warning_count) ?? 0;
625
- return `${warningCount} unallowed warning${warningCount === 1 ? "" : "s"}`;
625
+ const totalConsoleCount = cliFiniteNumber(evidence.total_console_warning_count);
626
+ const allowedConsoleCount = cliFiniteNumber(evidence.allowed_console_warning_count);
627
+ const allowedTextCount = Array.isArray(evidence.allowed_console_texts) ? evidence.allowed_console_texts.filter((value) => typeof value === "string" && value.trim()).length : 0;
628
+ const allowedPatternCount = Array.isArray(evidence.allowed_console_patterns) ? evidence.allowed_console_patterns.filter((value) => typeof value === "string" && value.trim()).length : 0;
629
+ const parts = [`${warningCount} unallowed warning${warningCount === 1 ? "" : "s"}`];
630
+ if (totalConsoleCount !== void 0 && allowedConsoleCount !== void 0) {
631
+ parts.push(`${allowedConsoleCount}/${totalConsoleCount} warning${totalConsoleCount === 1 ? "" : "s"} allowed`);
632
+ }
633
+ if (allowedTextCount || allowedPatternCount) {
634
+ parts.push(`allowlist ${allowedTextCount} text${allowedTextCount === 1 ? "" : "s"}, ${allowedPatternCount} pattern${allowedPatternCount === 1 ? "" : "s"}`);
635
+ }
636
+ return parts.join(", ");
626
637
  }
627
638
  return void 0;
628
639
  }
@@ -659,6 +670,100 @@ function cliReturnSummaryLabel(value) {
659
670
  }).filter(Boolean);
660
671
  return parts.length ? parts.join(", ") : void 0;
661
672
  }
673
+ function setupFailureObstructionSnippet(reason) {
674
+ if (!reason || !reason.includes("intercepts pointer events")) return void 0;
675
+ const line = reason.split(/\r?\n/).find((item) => item.includes("intercepts pointer events"));
676
+ const match = line?.match(/-\s+(.+?)\s+intercepts pointer events/);
677
+ const snippet = match?.[1]?.replace(/\s+/g, " ").trim();
678
+ return snippet || void 0;
679
+ }
680
+ function setupReceiptArray(viewport, key) {
681
+ return Array.isArray(viewport[key]) ? viewport[key].map(cliRecord).filter((item) => Boolean(item)) : [];
682
+ }
683
+ function setupReturnSummaryValue(receipt, names) {
684
+ for (const name of names) {
685
+ if (receipt[name] !== void 0) return receipt[name];
686
+ }
687
+ const returned = cliRecord(receipt.returned);
688
+ for (const name of names) {
689
+ if (returned?.[name] !== void 0) return returned[name];
690
+ }
691
+ const summaries = Array.isArray(receipt.return_summary) ? receipt.return_summary.map(cliRecord).filter((item) => Boolean(item)) : [];
692
+ for (const item of summaries) {
693
+ const label = cliString(item.label);
694
+ const path2 = cliString(item.path);
695
+ if (names.some((name) => label === name || path2 === name)) return item.value;
696
+ }
697
+ return void 0;
698
+ }
699
+ function setupMetricPart(receipts, label, names = [label]) {
700
+ for (const receipt of receipts) {
701
+ const value = setupReturnSummaryValue(receipt, names);
702
+ const valueLabel = cliValueLabel(value);
703
+ if (valueLabel !== void 0) return `${label}=${valueLabel}`;
704
+ }
705
+ return void 0;
706
+ }
707
+ function setupInputReceiptLabel(kind, receipt) {
708
+ const selector = cliString(receipt.selector);
709
+ const pointerType = cliString(receipt.pointer_type);
710
+ const inputDispatch = cliString(receipt.input_dispatch);
711
+ const key = cliString(receipt.key);
712
+ return [
713
+ kind,
714
+ selector ? markdownInlineCode(selector) : "",
715
+ pointerType ? markdownInlineCode(pointerType) : "",
716
+ key ? markdownInlineCode(key) : "",
717
+ inputDispatch ? `via ${markdownInlineCode(inputDispatch)}` : ""
718
+ ].filter(Boolean).join(" ");
719
+ }
720
+ function setupCanvasHashChangeLabel(receipts) {
721
+ const changed = receipts.find((receipt) => cliString(receipt.previous_hash) && cliString(receipt.hash) && receipt.changed === true);
722
+ if (changed) return `${markdownInlineCode(cliString(changed.previous_hash) || "")} -> ${markdownInlineCode(cliString(changed.hash) || "")}`;
723
+ const hashes = receipts.map((receipt) => cliString(receipt.hash)).filter((hash) => Boolean(hash));
724
+ const first = hashes[0];
725
+ const last = [...hashes].reverse().find((hash) => hash !== first);
726
+ return first && last ? `${markdownInlineCode(first)} -> ${markdownInlineCode(last)}` : void 0;
727
+ }
728
+ function setupNaturalInputSummaryMarkdown(viewports) {
729
+ const lines = [];
730
+ for (const viewport of viewports.slice(0, 8)) {
731
+ const name = cliString(viewport.name) || "viewport";
732
+ const inputReceipts = [
733
+ ...setupReceiptArray(viewport, "drag").map((receipt) => ({ kind: "drag", receipt })),
734
+ ...setupReceiptArray(viewport, "tap").map((receipt) => ({ kind: "tap", receipt })),
735
+ ...setupReceiptArray(viewport, "press").map((receipt) => ({ kind: "press", receipt }))
736
+ ].filter(({ receipt }) => receipt.ok !== false);
737
+ if (!inputReceipts.length) continue;
738
+ const valueReceipts = [
739
+ ...setupReceiptArray(viewport, "window_eval"),
740
+ ...setupReceiptArray(viewport, "window_call")
741
+ ].filter((receipt) => receipt.ok !== false);
742
+ const canvasReceipts = setupReceiptArray(viewport, "canvas_signature").filter((receipt) => receipt.ok !== false);
743
+ const eventParts = [
744
+ setupMetricPart(valueReceipts, "pointerDowns"),
745
+ setupMetricPart(valueReceipts, "pointerMoves"),
746
+ setupMetricPart(valueReceipts, "pointerUps"),
747
+ setupMetricPart(valueReceipts, "trustedEvents"),
748
+ setupMetricPart(valueReceipts, "eventCount")
749
+ ].filter((part) => Boolean(part));
750
+ const pixelParts = [
751
+ setupMetricPart(valueReceipts, "nonWhiteDelta"),
752
+ setupMetricPart(valueReceipts, "darkDelta")
753
+ ].filter((part) => Boolean(part));
754
+ const hashChange = setupCanvasHashChangeLabel(canvasReceipts);
755
+ if (!eventParts.length && !pixelParts.length && !hashChange) continue;
756
+ const inputText = inputReceipts.slice(0, 3).map(({ kind, receipt }) => setupInputReceiptLabel(kind, receipt)).join(", ");
757
+ const parts = [
758
+ inputText,
759
+ eventParts.length ? `events ${eventParts.join(", ")}` : "",
760
+ pixelParts.length ? `pixel deltas ${pixelParts.join(", ")}` : "",
761
+ hashChange ? `canvas hash ${hashChange}` : ""
762
+ ].filter(Boolean);
763
+ lines.push(`- natural input ${name}: ${parts.join("; ")}`);
764
+ }
765
+ return lines;
766
+ }
662
767
  function balancedSetupReceiptDetails(groups, limit) {
663
768
  if (limit <= 0) return [];
664
769
  const total = groups.reduce((sum, group) => sum + group.length, 0);
@@ -848,6 +953,7 @@ function profileSetupSummaryMarkdown(result) {
848
953
  if (canvasSignatureTotal) {
849
954
  lines.push(`- canvas_signature: ${canvasSignatureTotal} action(s)`);
850
955
  }
956
+ lines.push(...setupNaturalInputSummaryMarkdown(viewports));
851
957
  for (const viewport of viewports.slice(0, 8)) {
852
958
  const name = cliString(viewport.name) || "viewport";
853
959
  const ok = viewport.ok === false ? "failed" : "ok";
@@ -959,12 +1065,14 @@ function profileSetupSummaryMarkdown(result) {
959
1065
  }
960
1066
  const sampledPressDetails = balancedSetupReceiptDetails(pressGroups, 12);
961
1067
  for (const { name, receipt } of sampledPressDetails) {
1068
+ const action = cliString(receipt.action) || "press";
962
1069
  const key = cliString(receipt.key) || "key";
963
1070
  const selector = cliString(receipt.selector);
964
1071
  const frameSelector = cliString(receipt.frame_selector);
1072
+ const holdMs = cliFiniteNumber(receipt.hold_ms);
965
1073
  const ok = receipt.ok === false ? "failed" : "ok";
966
1074
  const reason = cliString(receipt.reason);
967
- lines.push(`- ${name} press: ${ok}, ${markdownInlineCode(key)}${selector ? ` on ${markdownInlineCode(selector)}` : ""}${frameSelector ? ` in frame ${markdownInlineCode(frameSelector)}` : ""}${reason ? `, reason ${markdownInlineCode(reason, 100)}` : ""}`);
1075
+ lines.push(`- ${name} ${action}: ${ok}, ${markdownInlineCode(key)}${selector ? ` on ${markdownInlineCode(selector)}` : ""}${frameSelector ? ` in frame ${markdownInlineCode(frameSelector)}` : ""}${holdMs === void 0 ? "" : `, held ${holdMs}ms`}${reason ? `, reason ${markdownInlineCode(reason, 100)}` : ""}`);
968
1076
  }
969
1077
  if (pressDetails.length > sampledPressDetails.length) lines.push(`- ${pressDetails.length - sampledPressDetails.length} additional press receipt(s) omitted.`);
970
1078
  const canvasSignatureGroups = viewports.map((viewport) => {
@@ -1127,7 +1235,11 @@ function profileSetupSummaryMarkdown(result) {
1127
1235
  const selector = cliString(failure.selector);
1128
1236
  const reason = cliString(failure.reason);
1129
1237
  const caseInsensitiveText = cliString(failure.case_insensitive_text);
1238
+ const obstruction = setupFailureObstructionSnippet(reason);
1130
1239
  lines.push(`- failed ${name}: ${action}${selector ? ` ${markdownInlineCode(selector)}` : ""}${reason ? ` reason ${markdownInlineCode(reason)}` : ""}${caseInsensitiveText ? `; case-insensitive sample ${markdownInlineCode(caseInsensitiveText, 140)}` : ""}`);
1240
+ if (obstruction) {
1241
+ lines.push(`- obstruction ${name}: target ${selector ? markdownInlineCode(selector) : markdownInlineCode(action)} intercepted by ${markdownInlineCode(obstruction, 120)}`);
1242
+ }
1131
1243
  }
1132
1244
  if (failedDetails.length > 8) lines.push(`- ${failedDetails.length - 8} additional failed setup action(s) omitted.`);
1133
1245
  if (viewports.length > 8) lines.push(`- ${viewports.length - 8} additional viewport(s) omitted from setup summary.`);
package/dist/index.cjs CHANGED
@@ -8780,6 +8780,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
8780
8780
  "tap",
8781
8781
  "drag",
8782
8782
  "press",
8783
+ "key_down",
8784
+ "key_up",
8783
8785
  "fill",
8784
8786
  "set_input_value",
8785
8787
  "set_range_value",
@@ -9345,12 +9347,14 @@ function profileSetupTapReceipts(results) {
9345
9347
  }));
9346
9348
  }
9347
9349
  function profileSetupPressReceipts(results) {
9348
- return results.filter((result) => profileSetupResultAction(result) === "press").map((result) => ({
9350
+ return results.filter((result) => ["press", "key_down", "key_up"].includes(profileSetupResultAction(result))).map((result) => ({
9351
+ action: profileSetupResultAction(result),
9349
9352
  ordinal: result.ordinal ?? null,
9350
9353
  ok: result.ok !== false,
9351
9354
  selector: result.selector ?? null,
9352
9355
  frame_selector: result.frame_selector ?? null,
9353
9356
  key: result.key ?? null,
9357
+ hold_ms: result.hold_ms ?? null,
9354
9358
  reason: result.reason ?? result.error ?? null
9355
9359
  }));
9356
9360
  }
@@ -9645,7 +9649,7 @@ function isSupportedCheckType(value) {
9645
9649
  }
9646
9650
  function normalizeSetupActionType(value, index) {
9647
9651
  const normalizedInput = String(value || "").trim().replace(/-/g, "_");
9648
- const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
9652
+ const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "keyboard_down" || normalizedInput === "key_down" || normalizedInput === "keydown" || normalizedInput === "press_down" ? "key_down" : normalizedInput === "keyboard_up" || normalizedInput === "key_up" || normalizedInput === "keyup" || normalizedInput === "press_up" || normalizedInput === "release_key" || normalizedInput === "key_release" ? "key_up" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
9649
9653
  if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
9650
9654
  return normalized;
9651
9655
  }
@@ -9816,6 +9820,11 @@ function normalizeSetupAction(input, index) {
9816
9820
  const toY = numberValue3(valueFromOwn(input, "to_y", "toY", "end_y", "endY", "y2"));
9817
9821
  const coordinateMode = normalizeSetupActionCoordinateMode(valueFromOwn(input, "coordinate_mode", "coordinateMode", "coords", "units"), index);
9818
9822
  const pointerType = normalizeSetupActionPointerType(valueFromOwn(input, "pointer_type", "pointerType", "input_type", "inputType"), type, index);
9823
+ const durationMs = numberValue3(input.duration_ms) ?? numberValue3(input.durationMs);
9824
+ const holdMs = type === "press" ? normalizeSetupActionNonNegativeNumber(input, index, "hold_ms", "hold_ms", "holdMs", "key_down_ms", "keyDownMs", "down_ms", "downMs") ?? durationMs : void 0;
9825
+ if (type === "press" && holdMs !== void 0 && (holdMs < 0 || holdMs > 3e4)) {
9826
+ throw new Error(`target.setup_actions[${index}].hold_ms must be a finite number from 0 to 30000.`);
9827
+ }
9819
9828
  if (type === "click") {
9820
9829
  const hasClickCoordinate = fromX !== void 0 || fromY !== void 0;
9821
9830
  if (hasClickCoordinate && (fromX === void 0 || fromY === void 0)) {
@@ -9896,7 +9905,7 @@ function normalizeSetupAction(input, index) {
9896
9905
  dialogAccept = true;
9897
9906
  }
9898
9907
  }
9899
- if (type === "press" && !key) {
9908
+ if ((type === "press" || type === "key_down" || type === "key_up") && !key) {
9900
9909
  throw new Error(`target.setup_actions[${index}] ${type} requires key.`);
9901
9910
  }
9902
9911
  if ((type === "local_storage" || type === "session_storage") && !key) {
@@ -9982,9 +9991,10 @@ function normalizeSetupAction(input, index) {
9982
9991
  from_y: fromY,
9983
9992
  to_x: toX,
9984
9993
  to_y: toY,
9985
- duration_ms: numberValue3(input.duration_ms) ?? numberValue3(input.durationMs),
9994
+ duration_ms: durationMs,
9986
9995
  steps,
9987
9996
  key,
9997
+ hold_ms: holdMs,
9988
9998
  value,
9989
9999
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
9990
10000
  random_queue: randomQueue,
@@ -13222,13 +13232,15 @@ function profileSetupTapReceipts(results) {
13222
13232
  }
13223
13233
  function profileSetupPressReceipts(results) {
13224
13234
  return (results || [])
13225
- .filter((result) => result && profileSetupResultAction(result) === "press")
13235
+ .filter((result) => result && ["press", "key_down", "key_up"].includes(profileSetupResultAction(result)))
13226
13236
  .map((result) => ({
13237
+ action: profileSetupResultAction(result),
13227
13238
  ordinal: result.ordinal ?? null,
13228
13239
  ok: result.ok !== false,
13229
13240
  selector: result.selector ?? null,
13230
13241
  frame_selector: result.frame_selector ?? null,
13231
13242
  key: result.key ?? null,
13243
+ hold_ms: result.hold_ms ?? null,
13232
13244
  reason: result.reason || result.error || null,
13233
13245
  }));
13234
13246
  }
@@ -15162,12 +15174,56 @@ async function executeSetupAction(action, ordinal, viewport) {
15162
15174
  duration_ms: durationMs || undefined,
15163
15175
  };
15164
15176
  }
15177
+ if (type === "key_down" || type === "key_up") {
15178
+ const key = String(action.key || "").trim();
15179
+ if (!key) return { ...base, reason: "missing_key" };
15180
+ const scope = await setupActionScope(action, timeout);
15181
+ if (!scope.ok) return setupScopeFailure(base, scope);
15182
+ let count;
15183
+ let targetIndex;
15184
+ if (action.selector) {
15185
+ const locator = scope.context.locator(action.selector);
15186
+ count = await locator.count();
15187
+ if (!count) return { ...base, reason: "selector_not_found", count, key };
15188
+ targetIndex = Number.isInteger(action.index) ? action.index : 0;
15189
+ if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
15190
+ await locator.nth(targetIndex).focus({ timeout }).catch(() => {});
15191
+ } else if (scope.frame_selector) {
15192
+ await scope.context.locator("body").focus({ timeout }).catch(() => {});
15193
+ }
15194
+ if (type === "key_down") {
15195
+ await page.keyboard.down(key);
15196
+ } else {
15197
+ await page.keyboard.up(key);
15198
+ }
15199
+ return {
15200
+ ...base,
15201
+ ...setupScopeEvidence(scope),
15202
+ ok: true,
15203
+ count,
15204
+ target_index: targetIndex,
15205
+ key,
15206
+ };
15207
+ }
15165
15208
  if (type === "press") {
15166
15209
  const key = String(action.key || "").trim();
15167
15210
  if (!key) return { ...base, reason: "missing_key" };
15211
+ const holdMs = Math.min(30000, Math.max(0, Math.floor(setupNumber(action.hold_ms ?? action.holdMs ?? action.key_down_ms ?? action.keyDownMs ?? action.down_ms ?? action.downMs ?? action.duration_ms ?? action.durationMs, 0) || 0)));
15168
15212
  const scope = await setupActionScope(action, timeout);
15169
15213
  if (!scope.ok) return setupScopeFailure(base, scope);
15170
15214
  if (!action.selector) {
15215
+ if (holdMs > 0) {
15216
+ if (scope.frame_selector) {
15217
+ await scope.context.locator("body").focus({ timeout }).catch(() => {});
15218
+ }
15219
+ await page.keyboard.down(key);
15220
+ try {
15221
+ await page.waitForTimeout(holdMs);
15222
+ } finally {
15223
+ await page.keyboard.up(key).catch(() => {});
15224
+ }
15225
+ return { ...base, ...setupScopeEvidence(scope), ok: true, key, hold_ms: holdMs };
15226
+ }
15171
15227
  if (scope.frame_selector) {
15172
15228
  await scope.context.locator("body").press(key, { timeout });
15173
15229
  } else {
@@ -15180,6 +15236,16 @@ async function executeSetupAction(action, ordinal, viewport) {
15180
15236
  if (!count) return { ...base, reason: "selector_not_found", count, key };
15181
15237
  const targetIndex = Number.isInteger(action.index) ? action.index : 0;
15182
15238
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
15239
+ if (holdMs > 0) {
15240
+ await locator.nth(targetIndex).focus({ timeout });
15241
+ await page.keyboard.down(key);
15242
+ try {
15243
+ await page.waitForTimeout(holdMs);
15244
+ } finally {
15245
+ await page.keyboard.up(key).catch(() => {});
15246
+ }
15247
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key, hold_ms: holdMs };
15248
+ }
15183
15249
  await locator.nth(targetIndex).press(key, { timeout });
15184
15250
  return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key };
15185
15251
  }
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ import {
62
62
  resolveRiddleProofProfileTimeoutSec,
63
63
  slugifyRiddleProofProfileName,
64
64
  summarizeRiddleProofProfileResult
65
- } from "./chunk-62JD4MJA.js";
65
+ } from "./chunk-XL7Q4M6D.js";
66
66
  import {
67
67
  DEFAULT_RIDDLE_API_BASE_URL,
68
68
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -94,6 +94,8 @@ var RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES = [
94
94
  "tap",
95
95
  "drag",
96
96
  "press",
97
+ "key_down",
98
+ "key_up",
97
99
  "fill",
98
100
  "set_input_value",
99
101
  "set_range_value",
@@ -659,12 +661,14 @@ function profileSetupTapReceipts(results) {
659
661
  }));
660
662
  }
661
663
  function profileSetupPressReceipts(results) {
662
- return results.filter((result) => profileSetupResultAction(result) === "press").map((result) => ({
664
+ return results.filter((result) => ["press", "key_down", "key_up"].includes(profileSetupResultAction(result))).map((result) => ({
665
+ action: profileSetupResultAction(result),
663
666
  ordinal: result.ordinal ?? null,
664
667
  ok: result.ok !== false,
665
668
  selector: result.selector ?? null,
666
669
  frame_selector: result.frame_selector ?? null,
667
670
  key: result.key ?? null,
671
+ hold_ms: result.hold_ms ?? null,
668
672
  reason: result.reason ?? result.error ?? null
669
673
  }));
670
674
  }
@@ -959,7 +963,7 @@ function isSupportedCheckType(value) {
959
963
  }
960
964
  function normalizeSetupActionType(value, index) {
961
965
  const normalizedInput = String(value || "").trim().replace(/-/g, "_");
962
- const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
966
+ const normalized = normalizedInput === "clear_browser_storage" ? "clear_storage" : normalizedInput === "reset_console" || normalizedInput === "clear_browser_console" || normalizedInput === "reset_browser_console" ? "clear_console" : normalizedInput === "pointer_drag" || normalizedInput === "mouse_drag" || normalizedInput === "drag_to" ? "drag" : normalizedInput === "pointer_tap" || normalizedInput === "touch_tap" || normalizedInput === "canvas_tap" ? "tap" : normalizedInput === "keyboard_press" || normalizedInput === "key_press" ? "press" : normalizedInput === "keyboard_down" || normalizedInput === "key_down" || normalizedInput === "keydown" || normalizedInput === "press_down" ? "key_down" : normalizedInput === "keyboard_up" || normalizedInput === "key_up" || normalizedInput === "keyup" || normalizedInput === "press_up" || normalizedInput === "release_key" || normalizedInput === "key_release" ? "key_up" : normalizedInput === "set_slider_value" || normalizedInput === "slider_value" || normalizedInput === "set_slider" || normalizedInput === "set_range" || normalizedInput === "range_value" || normalizedInput === "range_input" || normalizedInput === "set_range_input" ? "set_range_value" : normalizedInput === "deterministic_runtime" || normalizedInput === "mock_runtime" || normalizedInput === "mock_random" || normalizedInput === "mock_random_queue" || normalizedInput === "seed_random_queue" || normalizedInput === "set_random_queue" || normalizedInput === "mock_clock" || normalizedInput === "set_mock_clock" || normalizedInput === "set_runtime_determinism" || normalizedInput === "runtime_determinism" ? "deterministic_runtime" : normalizedInput === "canvas_hash" || normalizedInput === "capture_canvas_hash" || normalizedInput === "capture_canvas_signature" || normalizedInput === "canvas_state_signature" ? "canvas_signature" : normalizedInput === "capture_screenshot" || normalizedInput === "save_screenshot" || normalizedInput === "setup_screenshot" ? "screenshot" : normalizedInput === "accept_dialog" || normalizedInput === "accept_dialogs" || normalizedInput === "confirm_dialog" || normalizedInput === "set_dialog_response" ? "dialog_response" : normalizedInput === "dismiss_dialog" || normalizedInput === "dismiss_dialogs" || normalizedInput === "cancel_dialog" ? "dialog_response" : normalizedInput === "window_call_until" || normalizedInput === "call_until" || normalizedInput === "window_call_repeat_until" || normalizedInput === "repeat_window_call_until" ? "window_call_until" : normalizedInput === "window_evaluate" || normalizedInput === "browser_eval" || normalizedInput === "browser_evaluate" || normalizedInput === "evaluate_script" || normalizedInput === "profile_script" ? "window_eval" : normalizedInput;
963
967
  if (RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES.includes(normalized)) {
964
968
  return normalized;
965
969
  }
@@ -1130,6 +1134,11 @@ function normalizeSetupAction(input, index) {
1130
1134
  const toY = numberValue(valueFromOwn(input, "to_y", "toY", "end_y", "endY", "y2"));
1131
1135
  const coordinateMode = normalizeSetupActionCoordinateMode(valueFromOwn(input, "coordinate_mode", "coordinateMode", "coords", "units"), index);
1132
1136
  const pointerType = normalizeSetupActionPointerType(valueFromOwn(input, "pointer_type", "pointerType", "input_type", "inputType"), type, index);
1137
+ const durationMs = numberValue(input.duration_ms) ?? numberValue(input.durationMs);
1138
+ const holdMs = type === "press" ? normalizeSetupActionNonNegativeNumber(input, index, "hold_ms", "hold_ms", "holdMs", "key_down_ms", "keyDownMs", "down_ms", "downMs") ?? durationMs : void 0;
1139
+ if (type === "press" && holdMs !== void 0 && (holdMs < 0 || holdMs > 3e4)) {
1140
+ throw new Error(`target.setup_actions[${index}].hold_ms must be a finite number from 0 to 30000.`);
1141
+ }
1133
1142
  if (type === "click") {
1134
1143
  const hasClickCoordinate = fromX !== void 0 || fromY !== void 0;
1135
1144
  if (hasClickCoordinate && (fromX === void 0 || fromY === void 0)) {
@@ -1210,7 +1219,7 @@ function normalizeSetupAction(input, index) {
1210
1219
  dialogAccept = true;
1211
1220
  }
1212
1221
  }
1213
- if (type === "press" && !key) {
1222
+ if ((type === "press" || type === "key_down" || type === "key_up") && !key) {
1214
1223
  throw new Error(`target.setup_actions[${index}] ${type} requires key.`);
1215
1224
  }
1216
1225
  if ((type === "local_storage" || type === "session_storage") && !key) {
@@ -1296,9 +1305,10 @@ function normalizeSetupAction(input, index) {
1296
1305
  from_y: fromY,
1297
1306
  to_x: toX,
1298
1307
  to_y: toY,
1299
- duration_ms: numberValue(input.duration_ms) ?? numberValue(input.durationMs),
1308
+ duration_ms: durationMs,
1300
1309
  steps,
1301
1310
  key,
1311
+ hold_ms: holdMs,
1302
1312
  value,
1303
1313
  value_json: hasJsonValue ? toJsonValue(input.value_json ?? input.valueJson ?? input.json) : void 0,
1304
1314
  random_queue: randomQueue,
@@ -4536,13 +4546,15 @@ function profileSetupTapReceipts(results) {
4536
4546
  }
4537
4547
  function profileSetupPressReceipts(results) {
4538
4548
  return (results || [])
4539
- .filter((result) => result && profileSetupResultAction(result) === "press")
4549
+ .filter((result) => result && ["press", "key_down", "key_up"].includes(profileSetupResultAction(result)))
4540
4550
  .map((result) => ({
4551
+ action: profileSetupResultAction(result),
4541
4552
  ordinal: result.ordinal ?? null,
4542
4553
  ok: result.ok !== false,
4543
4554
  selector: result.selector ?? null,
4544
4555
  frame_selector: result.frame_selector ?? null,
4545
4556
  key: result.key ?? null,
4557
+ hold_ms: result.hold_ms ?? null,
4546
4558
  reason: result.reason || result.error || null,
4547
4559
  }));
4548
4560
  }
@@ -6476,12 +6488,56 @@ async function executeSetupAction(action, ordinal, viewport) {
6476
6488
  duration_ms: durationMs || undefined,
6477
6489
  };
6478
6490
  }
6491
+ if (type === "key_down" || type === "key_up") {
6492
+ const key = String(action.key || "").trim();
6493
+ if (!key) return { ...base, reason: "missing_key" };
6494
+ const scope = await setupActionScope(action, timeout);
6495
+ if (!scope.ok) return setupScopeFailure(base, scope);
6496
+ let count;
6497
+ let targetIndex;
6498
+ if (action.selector) {
6499
+ const locator = scope.context.locator(action.selector);
6500
+ count = await locator.count();
6501
+ if (!count) return { ...base, reason: "selector_not_found", count, key };
6502
+ targetIndex = Number.isInteger(action.index) ? action.index : 0;
6503
+ if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
6504
+ await locator.nth(targetIndex).focus({ timeout }).catch(() => {});
6505
+ } else if (scope.frame_selector) {
6506
+ await scope.context.locator("body").focus({ timeout }).catch(() => {});
6507
+ }
6508
+ if (type === "key_down") {
6509
+ await page.keyboard.down(key);
6510
+ } else {
6511
+ await page.keyboard.up(key);
6512
+ }
6513
+ return {
6514
+ ...base,
6515
+ ...setupScopeEvidence(scope),
6516
+ ok: true,
6517
+ count,
6518
+ target_index: targetIndex,
6519
+ key,
6520
+ };
6521
+ }
6479
6522
  if (type === "press") {
6480
6523
  const key = String(action.key || "").trim();
6481
6524
  if (!key) return { ...base, reason: "missing_key" };
6525
+ const holdMs = Math.min(30000, Math.max(0, Math.floor(setupNumber(action.hold_ms ?? action.holdMs ?? action.key_down_ms ?? action.keyDownMs ?? action.down_ms ?? action.downMs ?? action.duration_ms ?? action.durationMs, 0) || 0)));
6482
6526
  const scope = await setupActionScope(action, timeout);
6483
6527
  if (!scope.ok) return setupScopeFailure(base, scope);
6484
6528
  if (!action.selector) {
6529
+ if (holdMs > 0) {
6530
+ if (scope.frame_selector) {
6531
+ await scope.context.locator("body").focus({ timeout }).catch(() => {});
6532
+ }
6533
+ await page.keyboard.down(key);
6534
+ try {
6535
+ await page.waitForTimeout(holdMs);
6536
+ } finally {
6537
+ await page.keyboard.up(key).catch(() => {});
6538
+ }
6539
+ return { ...base, ...setupScopeEvidence(scope), ok: true, key, hold_ms: holdMs };
6540
+ }
6485
6541
  if (scope.frame_selector) {
6486
6542
  await scope.context.locator("body").press(key, { timeout });
6487
6543
  } else {
@@ -6494,6 +6550,16 @@ async function executeSetupAction(action, ordinal, viewport) {
6494
6550
  if (!count) return { ...base, reason: "selector_not_found", count, key };
6495
6551
  const targetIndex = Number.isInteger(action.index) ? action.index : 0;
6496
6552
  if (targetIndex < 0 || targetIndex >= count) return { ...base, reason: "index_out_of_range", count, target_index: targetIndex, key };
6553
+ if (holdMs > 0) {
6554
+ await locator.nth(targetIndex).focus({ timeout });
6555
+ await page.keyboard.down(key);
6556
+ try {
6557
+ await page.waitForTimeout(holdMs);
6558
+ } finally {
6559
+ await page.keyboard.up(key).catch(() => {});
6560
+ }
6561
+ return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key, hold_ms: holdMs };
6562
+ }
6497
6563
  await locator.nth(targetIndex).press(key, { timeout });
6498
6564
  return { ...base, ...setupScopeEvidence(scope), ok: true, count, target_index: targetIndex, key };
6499
6565
  }
@@ -5,7 +5,7 @@ declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evide
5
5
  declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
6
6
  declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
7
7
  declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "dialog_count_equals", "dialog_accept_count_equals", "dialog_dismiss_count_equals", "selector_text_visible", "selector_text_absent", "selector_text_order", "observe_within", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors", "no_console_warnings"];
8
- declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "tap", "drag", "press", "fill", "set_input_value", "set_range_value", "deterministic_runtime", "canvas_signature", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_eval", "window_call", "window_call_until"];
8
+ declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "tap", "drag", "press", "key_down", "key_up", "fill", "set_input_value", "set_range_value", "deterministic_runtime", "canvas_signature", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_eval", "window_call", "window_call_until"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
11
11
  type RiddleProofProfileSetupActionType = typeof RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES[number];
@@ -124,6 +124,7 @@ interface RiddleProofProfileSetupAction {
124
124
  duration_ms?: number;
125
125
  steps?: number;
126
126
  key?: string;
127
+ hold_ms?: number;
127
128
  value?: string;
128
129
  value_json?: JsonValue;
129
130
  random_queue?: number[];
package/dist/profile.d.ts CHANGED
@@ -5,7 +5,7 @@ declare const RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION: "riddle-proof.profile-evide
5
5
  declare const RIDDLE_PROOF_PROFILE_RESULT_VERSION: "riddle-proof.profile-result.v1";
6
6
  declare const RIDDLE_PROOF_PROFILE_STATUSES: readonly ["passed", "product_regression", "proof_insufficient", "environment_blocked", "configuration_error", "needs_human_review"];
7
7
  declare const RIDDLE_PROOF_PROFILE_CHECK_TYPES: readonly ["route_loaded", "url_search_param_equals", "url_search_param_absent", "selector_visible", "selector_absent", "selector_count_at_least", "selector_count_equals", "selector_count_equal", "selector_count_eq", "dialog_count_equals", "dialog_accept_count_equals", "dialog_dismiss_count_equals", "selector_text_visible", "selector_text_absent", "selector_text_order", "observe_within", "frame_text_visible", "frame_url_equals", "frame_url_matches", "frame_no_horizontal_overflow", "text_visible", "text_absent", "http_status", "link_status", "artifact_link_status", "route_inventory", "no_horizontal_overflow", "no_mobile_horizontal_overflow", "no_fatal_console_errors", "no_console_warnings"];
8
- declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "tap", "drag", "press", "fill", "set_input_value", "set_range_value", "deterministic_runtime", "canvas_signature", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_eval", "window_call", "window_call_until"];
8
+ declare const RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES: readonly ["click", "tap", "drag", "press", "key_down", "key_up", "fill", "set_input_value", "set_range_value", "deterministic_runtime", "canvas_signature", "assert_text_visible", "assert_text_absent", "assert_selector_count", "assert_window_value", "assert_window_number", "local_storage", "session_storage", "clear_storage", "clear_console", "dialog_response", "screenshot", "wait", "wait_for_selector", "wait_for_text", "window_eval", "window_call", "window_call_until"];
9
9
  type RiddleProofProfileStatus = typeof RIDDLE_PROOF_PROFILE_STATUSES[number];
10
10
  type RiddleProofProfileCheckType = typeof RIDDLE_PROOF_PROFILE_CHECK_TYPES[number];
11
11
  type RiddleProofProfileSetupActionType = typeof RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES[number];
@@ -124,6 +124,7 @@ interface RiddleProofProfileSetupAction {
124
124
  duration_ms?: number;
125
125
  steps?: number;
126
126
  key?: string;
127
+ hold_ms?: number;
127
128
  value?: string;
128
129
  value_json?: JsonValue;
129
130
  random_queue?: number[];
package/dist/profile.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  resolveRiddleProofProfileTimeoutSec,
24
24
  slugifyRiddleProofProfileName,
25
25
  summarizeRiddleProofProfileResult
26
- } from "./chunk-62JD4MJA.js";
26
+ } from "./chunk-XL7Q4M6D.js";
27
27
  export {
28
28
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
29
29
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.180",
3
+ "version": "0.7.182",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",