@deeeed/metamask-harness 0.18.0 → 0.19.0

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/README.md +4 -7
  3. package/adapters/core/inject.sh +1 -6
  4. package/adapters/extension/inject.mjs +1 -4
  5. package/adapters/extension/verify.sh +1 -1
  6. package/adapters/mobile/inject.sh +5 -4
  7. package/adapters/mobile/verify.sh +15 -19
  8. package/adapters/shared/harness-source-fingerprint.mjs +14 -12
  9. package/dist/adapters/harness-freshness.js +49 -0
  10. package/dist/cli.js +0 -4
  11. package/dist/command-contract.js +51 -40
  12. package/dist/command-journal.js +50 -12
  13. package/dist/commands/call.js +95 -20
  14. package/dist/commands/check.js +1 -1
  15. package/dist/commands/completion-candidates.js +7 -19
  16. package/dist/commands/fixtures.js +50 -1
  17. package/dist/commands/last.js +9 -1
  18. package/dist/commands/launch/extension.js +2 -2
  19. package/dist/commands/launch/mobile.js +2 -0
  20. package/dist/commands/list-executables.js +56 -20
  21. package/dist/commands/manifest.js +28 -12
  22. package/dist/commands/parse-args.js +55 -3
  23. package/dist/commands/provision.js +0 -1
  24. package/dist/commands/run-engine.js +514 -336
  25. package/dist/commands/run.js +25 -37
  26. package/dist/commands/shared.js +1 -1
  27. package/dist/heal-bounds.js +5 -0
  28. package/dist/mm-harness-cli.js +22 -30
  29. package/dist/run-diagnostics.js +1 -1
  30. package/dist/run-recording.js +1 -1
  31. package/dist/runner.js +24 -2
  32. package/docs/CONTRIBUTING.md +2 -3
  33. package/docs/QA.md +0 -1
  34. package/docs/RECIPES.md +52 -91
  35. package/library/README.md +5 -5
  36. package/library/actions/core/perps/_controller.mjs +33 -1
  37. package/library/actions/core/perps/assert_orders.mjs +6 -7
  38. package/library/actions/core/perps/assert_positions.mjs +6 -7
  39. package/library/actions/core/perps/close_orders.mjs +2 -0
  40. package/library/actions/core/perps/close_positions.mjs +2 -0
  41. package/library/actions/core/perps/ensure_orders.mjs +4 -2
  42. package/library/actions/core/perps/ensure_positions.mjs +4 -2
  43. package/library/actions/core/perps/place_order.mjs +7 -3
  44. package/library/actions/extension/perps/assert_orders.mjs +2 -1
  45. package/library/actions/extension/perps/assert_positions.mjs +2 -1
  46. package/library/actions/extension/perps/perps.mjs +43 -14
  47. package/library/actions/mobile/perps/assert_orders.mjs +2 -1
  48. package/library/actions/mobile/perps/assert_positions.mjs +2 -1
  49. package/library/actions/mobile/perps/perps.mjs +40 -12
  50. package/library/library.json +1 -1
  51. package/library/manifests/core.action-manifest.json +1170 -413
  52. package/library/manifests/extension.action-manifest.json +1495 -641
  53. package/library/manifests/mobile.action-manifest.json +1704 -769
  54. package/library/recipes/app/lifecycle.android-smoke.mobile.recipe.json +63 -81
  55. package/library/recipes/perps/clean-market-testnet.core.recipe.json +44 -0
  56. package/library/recipes/perps/clean-market-testnet.recipe.json +49 -0
  57. package/library/recipes/perps/lifecycle.recipe.json +136 -180
  58. package/library/recipes/perps/order-lifecycle.core.recipe.json +71 -67
  59. package/library/recipes/perps/performance.background-resume.mobile.recipe.json +51 -67
  60. package/library/recipes/perps/performance.cold-start.mobile.recipe.json +51 -67
  61. package/library/recipes/perps/performance.mobile.recipe.json +37 -51
  62. package/library/recipes/perps/performance.warm-start.mobile.recipe.json +44 -59
  63. package/library/recipes/perps/read-markets.core.recipe.json +29 -31
  64. package/library/recipes/perps/smoke.core.recipe.json +29 -32
  65. package/library/recipes/perps/smoke.extension.recipe.json +41 -44
  66. package/library/recipes/perps/smoke.mobile.recipe.json +42 -44
  67. package/library/recipes/perps/trading-lifecycle.core.recipe.json +69 -65
  68. package/library/recipes/runner/action-validation.extension.recipe.json +312 -405
  69. package/library/recipes/runner/action-validation.mobile.recipe.json +316 -409
  70. package/library/recipes/runner/smoke.core.recipe.json +18 -20
  71. package/library/recipes/runner/smoke.extension.recipe.json +23 -24
  72. package/library/recipes/runner/smoke.mobile.recipe.json +23 -24
  73. package/library/recipes/wallet/smoke.extension.recipe.json +33 -35
  74. package/library/recipes/wallet/smoke.mobile.recipe.json +33 -35
  75. package/package.json +3 -3
  76. package/scripts/completions.sh +1 -4
  77. package/dist/adapters/extension/harness-freshness.js +0 -39
  78. package/dist/commands/flows.js +0 -91
  79. package/library/flows/perps.flows.json +0 -64
@@ -12,6 +12,7 @@
12
12
  "index_artifacts",
13
13
  "end",
14
14
  "call",
15
+ "switch",
15
16
  "ui.navigate",
16
17
  "ui.press",
17
18
  "ui.key_press",
@@ -35,7 +36,28 @@
35
36
  "intent": "Run a shell command from the project root"
36
37
  }
37
38
  }
38
- ]
39
+ ],
40
+ "schema": {
41
+ "type": "object",
42
+ "properties": {
43
+ "cmd": {
44
+ "type": "string"
45
+ },
46
+ "command": {
47
+ "type": "string"
48
+ },
49
+ "cwd": {
50
+ "type": "string"
51
+ },
52
+ "timeout_ms": {
53
+ "type": "number"
54
+ },
55
+ "allow_failure": {
56
+ "type": "boolean"
57
+ }
58
+ },
59
+ "additionalProperties": false
60
+ }
39
61
  },
40
62
  "wait": {
41
63
  "description": "Base wait action.",
@@ -47,7 +69,44 @@
47
69
  "intent": "Base wait action"
48
70
  }
49
71
  }
50
- ]
72
+ ],
73
+ "schema": {
74
+ "type": "object",
75
+ "properties": {
76
+ "duration_ms": {
77
+ "type": "integer"
78
+ },
79
+ "ms": {
80
+ "type": "integer"
81
+ }
82
+ },
83
+ "additionalProperties": false
84
+ }
85
+ },
86
+ "switch": {
87
+ "description": "Choose a graph branch when one resolved string equals another.",
88
+ "examples": [
89
+ {
90
+ "node": {
91
+ "action": "switch",
92
+ "value": "{{params.mode}}",
93
+ "equals": "background_resume",
94
+ "cases": { "match": "background" },
95
+ "default": "warm",
96
+ "intent": "Choose the requested lifecycle preparation path"
97
+ }
98
+ }
99
+ ],
100
+ "schema": {
101
+ "type": "object",
102
+ "properties": {
103
+ "value": { "type": "string" },
104
+ "equals": { "type": "string" }
105
+ },
106
+ "required": ["value", "equals"],
107
+ "additionalProperties": false
108
+ },
109
+ "result_cases": ["match"]
51
110
  },
52
111
  "watch_logs": {
53
112
  "description": "Base log assertion/capture action.",
@@ -59,7 +118,29 @@
59
118
  "intent": "Base log assertion/capture action"
60
119
  }
61
120
  }
62
- ]
121
+ ],
122
+ "schema": {
123
+ "type": "object",
124
+ "properties": {
125
+ "path": {
126
+ "type": "string"
127
+ },
128
+ "contains": {
129
+ "type": "string"
130
+ },
131
+ "scope": {
132
+ "type": "string",
133
+ "enum": [
134
+ "run",
135
+ "file"
136
+ ]
137
+ }
138
+ },
139
+ "required": [
140
+ "path"
141
+ ],
142
+ "additionalProperties": false
143
+ }
63
144
  },
64
145
  "index_artifacts": {
65
146
  "description": "Index extra recipe-authored artifacts that already exist on disk. Do not use after ui.screenshot; screenshots are registered by the runner.",
@@ -78,7 +159,55 @@
78
159
  "intent": "Register a pre-existing report artifact"
79
160
  }
80
161
  }
81
- ]
162
+ ],
163
+ "schema": {
164
+ "type": "object",
165
+ "properties": {
166
+ "artifacts": {
167
+ "type": "array",
168
+ "items": {
169
+ "type": [
170
+ "string",
171
+ "object"
172
+ ],
173
+ "properties": {
174
+ "path": {
175
+ "type": "string"
176
+ },
177
+ "type": {
178
+ "type": "string"
179
+ },
180
+ "label": {
181
+ "type": "string"
182
+ },
183
+ "nodeId": {
184
+ "type": "string"
185
+ },
186
+ "mimeType": {
187
+ "type": "string"
188
+ },
189
+ "category": {
190
+ "type": "string"
191
+ },
192
+ "proofTarget": {
193
+ "type": "string"
194
+ },
195
+ "covers": {
196
+ "type": "array",
197
+ "items": {
198
+ "type": "string"
199
+ }
200
+ }
201
+ },
202
+ "additionalProperties": false
203
+ }
204
+ }
205
+ },
206
+ "required": [
207
+ "artifacts"
208
+ ],
209
+ "additionalProperties": false
210
+ }
82
211
  },
83
212
  "end": {
84
213
  "description": "Base terminal action.",
@@ -102,30 +231,37 @@
102
231
  "home",
103
232
  "perps",
104
233
  "perps-market"
105
- ],
106
- "description": "Small stable page aliases; use raw route/hash when you need a route outside this list."
234
+ ]
107
235
  },
108
236
  "market": {
109
- "type": "string",
110
- "description": "Market symbol for page=perps-market, e.g. ETH."
237
+ "type": "string"
111
238
  },
112
239
  "symbol": {
113
- "type": "string",
114
- "description": "Alias for market."
240
+ "type": "string"
241
+ },
242
+ "url": {
243
+ "type": "string"
244
+ },
245
+ "hash": {
246
+ "type": "string"
247
+ },
248
+ "path": {
249
+ "type": "string"
115
250
  },
116
251
  "route": {
117
- "type": "string",
118
- "description": "Raw React Navigation route."
252
+ "type": "string"
119
253
  },
120
254
  "screen": {
121
- "type": "string",
122
- "description": "Alias for route."
255
+ "type": "string"
123
256
  },
124
257
  "params": {
125
- "type": "object",
126
- "description": "Raw React Navigation params."
258
+ "type": "object"
259
+ },
260
+ "timeout_ms": {
261
+ "type": "number"
127
262
  }
128
- }
263
+ },
264
+ "additionalProperties": false
129
265
  },
130
266
  "examples": [
131
267
  {
@@ -160,18 +296,29 @@
160
296
  "schema": {
161
297
  "type": "object",
162
298
  "properties": {
163
- "action": { "const": "ui.press" },
164
- "test_id": { "type": "string", "minLength": 1 },
165
- "testID": { "type": "string", "minLength": 1 },
166
- "selector": { "type": "string", "minLength": 1 },
167
- "text": { "type": "string", "minLength": 1 }
299
+ "selector": {
300
+ "type": "string"
301
+ },
302
+ "test_id": {
303
+ "type": "string"
304
+ },
305
+ "testID": {
306
+ "type": "string"
307
+ },
308
+ "text": {
309
+ "type": "string"
310
+ },
311
+ "label": {
312
+ "type": "string"
313
+ },
314
+ "timeout_ms": {
315
+ "type": "number"
316
+ },
317
+ "settle": {
318
+ "type": "boolean"
319
+ }
168
320
  },
169
- "oneOf": [
170
- { "required": ["test_id"] },
171
- { "required": ["testID"] },
172
- { "required": ["selector"] },
173
- { "required": ["text"] }
174
- ]
321
+ "additionalProperties": false
175
322
  },
176
323
  "examples": [
177
324
  {
@@ -212,23 +359,94 @@
212
359
  }
213
360
  }
214
361
  ],
215
- "proof_effect": "E2E validation must include a passing ui-scroll-into-view trace event with output.intoView=true."
362
+ "proof_effect": "E2E validation must include a passing ui-scroll-into-view trace event with output.intoView=true.",
363
+ "schema": {
364
+ "type": "object",
365
+ "properties": {
366
+ "selector": {
367
+ "type": "string"
368
+ },
369
+ "test_id": {
370
+ "type": "string"
371
+ },
372
+ "testID": {
373
+ "type": "string"
374
+ },
375
+ "delta_x": {
376
+ "type": "number"
377
+ },
378
+ "delta_y": {
379
+ "type": "number"
380
+ },
381
+ "deltaX": {
382
+ "type": "number"
383
+ },
384
+ "deltaY": {
385
+ "type": "number"
386
+ },
387
+ "offset": {
388
+ "type": "number"
389
+ },
390
+ "scroll_into_view": {
391
+ "type": "boolean"
392
+ },
393
+ "into_view": {
394
+ "type": "boolean"
395
+ },
396
+ "animated": {
397
+ "type": "boolean"
398
+ },
399
+ "timeout_ms": {
400
+ "type": "number"
401
+ },
402
+ "settle": {
403
+ "type": "boolean"
404
+ }
405
+ },
406
+ "additionalProperties": false
407
+ }
216
408
  },
217
409
  "ui.wait_for": {
218
410
  "description": "Wait for a mounted React Native testID/test_id. Optional text validates text returned by that testID; text alone is not supported on Mobile.",
219
411
  "schema": {
220
412
  "type": "object",
221
413
  "properties": {
222
- "action": { "const": "ui.wait_for" },
223
- "test_id": { "type": "string" },
224
- "text": { "type": "string" },
414
+ "selector": {
415
+ "type": "string"
416
+ },
417
+ "test_id": {
418
+ "type": "string"
419
+ },
420
+ "testID": {
421
+ "type": "string"
422
+ },
423
+ "text": {
424
+ "type": "string"
425
+ },
426
+ "text_match": {
427
+ "type": "string"
428
+ },
429
+ "textMatch": {
430
+ "type": "string"
431
+ },
225
432
  "expected": {
226
433
  "type": "string",
227
- "enum": ["present", "absent", "hidden", "not_present"]
434
+ "enum": [
435
+ "present",
436
+ "visible",
437
+ "absent",
438
+ "hidden",
439
+ "not_present"
440
+ ]
228
441
  },
229
- "timeout_ms": { "type": "number" }
442
+ "visible": {
443
+ "type": "boolean"
444
+ },
445
+ "timeout_ms": {
446
+ "type": "number"
447
+ }
230
448
  },
231
- "required": ["action", "test_id"]
449
+ "additionalProperties": false
232
450
  },
233
451
  "examples": [
234
452
  {
@@ -236,7 +454,7 @@
236
454
  "action": "ui.wait_for",
237
455
  "test_id": "perps-home-heading",
238
456
  "expected": "present",
239
- "intent": "Wait for a React Native testID to be mounted"
457
+ "intent": "Confirm the requested mobile content is visible"
240
458
  }
241
459
  },
242
460
  {
@@ -244,7 +462,7 @@
244
462
  "action": "ui.wait_for",
245
463
  "test_id": "conditional-banner",
246
464
  "expected": "absent",
247
- "intent": "Prove a React Native testID is not mounted"
465
+ "intent": "Confirm the conditional mobile content is absent"
248
466
  }
249
467
  }
250
468
  ]
@@ -259,7 +477,25 @@
259
477
  "intent": "Capture visual evidence after a wait/assert step"
260
478
  }
261
479
  }
262
- ]
480
+ ],
481
+ "schema": {
482
+ "type": "object",
483
+ "properties": {
484
+ "path": {
485
+ "type": "string"
486
+ },
487
+ "label": {
488
+ "type": "string"
489
+ },
490
+ "category": {
491
+ "type": "string"
492
+ },
493
+ "timeout_ms": {
494
+ "type": "number"
495
+ }
496
+ },
497
+ "additionalProperties": false
498
+ }
263
499
  },
264
500
  "app.lifecycle": {
265
501
  "description": "Drive outer mobile app lifecycle for deterministic performance start states without rebuilding: foreground/launch, background, terminate, or restart the installed app.",
@@ -272,7 +508,101 @@
272
508
  "intent": "Send the selected Android device to the launcher before a warm-resume measurement"
273
509
  }
274
510
  }
275
- ]
511
+ ],
512
+ "schema": {
513
+ "type": "object",
514
+ "properties": {
515
+ "command": {
516
+ "type": "string",
517
+ "enum": [
518
+ "launch",
519
+ "foreground",
520
+ "background",
521
+ "terminate",
522
+ "restart"
523
+ ]
524
+ },
525
+ "event": {
526
+ "type": "string",
527
+ "enum": [
528
+ "launch",
529
+ "foreground",
530
+ "background",
531
+ "terminate",
532
+ "restart"
533
+ ]
534
+ },
535
+ "state": {
536
+ "type": "string",
537
+ "enum": [
538
+ "launch",
539
+ "foreground",
540
+ "background",
541
+ "terminate",
542
+ "restart"
543
+ ]
544
+ },
545
+ "settle_ms": {
546
+ "type": "integer"
547
+ },
548
+ "timeout_ms": {
549
+ "type": "integer"
550
+ },
551
+ "platform": {
552
+ "type": "string",
553
+ "enum": [
554
+ "ios",
555
+ "android"
556
+ ]
557
+ },
558
+ "watcher_port": {
559
+ "type": "number"
560
+ },
561
+ "metro_port": {
562
+ "type": "number"
563
+ },
564
+ "launch_url": {
565
+ "type": "string"
566
+ },
567
+ "launchUrl": {
568
+ "type": "string"
569
+ },
570
+ "url": {
571
+ "type": "string"
572
+ },
573
+ "adb_serial": {
574
+ "type": "string"
575
+ },
576
+ "android_device": {
577
+ "type": "string"
578
+ },
579
+ "device": {
580
+ "type": "string"
581
+ },
582
+ "package_id": {
583
+ "type": "string"
584
+ },
585
+ "packageName": {
586
+ "type": "string"
587
+ },
588
+ "app_id": {
589
+ "type": "string"
590
+ },
591
+ "simulator": {
592
+ "type": "string"
593
+ },
594
+ "ios_simulator": {
595
+ "type": "string"
596
+ },
597
+ "bundle_id": {
598
+ "type": "string"
599
+ },
600
+ "bundleId": {
601
+ "type": "string"
602
+ }
603
+ },
604
+ "additionalProperties": false
605
+ }
276
606
  },
277
607
  "assert_file": {
278
608
  "description": "Assert that a project file exists and optionally contains text.",
@@ -285,16 +615,31 @@
285
615
  "intent": "Assert that a project file exists and optionally contains text"
286
616
  }
287
617
  }
288
- ]
289
- },
290
- "assert_json": {
291
- "description": "Assert a JSON file value with a JSONPath-style selector.",
292
- "examples": [
293
- {
294
- "node": {
295
- "action": "assert_json",
296
- "path": "reports/result.json",
297
- "assert": {
618
+ ],
619
+ "schema": {
620
+ "type": "object",
621
+ "properties": {
622
+ "path": {
623
+ "type": "string"
624
+ },
625
+ "contains": {
626
+ "type": "string"
627
+ }
628
+ },
629
+ "required": [
630
+ "path"
631
+ ],
632
+ "additionalProperties": false
633
+ }
634
+ },
635
+ "assert_json": {
636
+ "description": "Assert a JSON file value with a JSONPath-style selector.",
637
+ "examples": [
638
+ {
639
+ "node": {
640
+ "action": "assert_json",
641
+ "path": "reports/result.json",
642
+ "assert": {
298
643
  "path": "$.status",
299
644
  "operator": "eq",
300
645
  "value": "ok"
@@ -302,7 +647,69 @@
302
647
  "intent": "Assert a JSON file value with a JSONPath-style selector"
303
648
  }
304
649
  }
305
- ]
650
+ ],
651
+ "schema": {
652
+ "type": "object",
653
+ "properties": {
654
+ "path": {
655
+ "type": "string"
656
+ },
657
+ "assert": {
658
+ "type": "object",
659
+ "properties": {
660
+ "path": {
661
+ "type": "string"
662
+ },
663
+ "operator": {
664
+ "type": "string"
665
+ },
666
+ "value": {
667
+ "type": [
668
+ "string",
669
+ "number",
670
+ "integer",
671
+ "boolean",
672
+ "object",
673
+ "array"
674
+ ],
675
+ "items": {
676
+ "type": [
677
+ "string",
678
+ "number",
679
+ "integer",
680
+ "boolean",
681
+ "object"
682
+ ]
683
+ }
684
+ },
685
+ "all": {
686
+ "type": "array",
687
+ "items": {
688
+ "type": "object"
689
+ }
690
+ },
691
+ "any": {
692
+ "type": "array",
693
+ "items": {
694
+ "type": "object"
695
+ }
696
+ },
697
+ "none": {
698
+ "type": "array",
699
+ "items": {
700
+ "type": "object"
701
+ }
702
+ }
703
+ },
704
+ "additionalProperties": false
705
+ }
706
+ },
707
+ "required": [
708
+ "path",
709
+ "assert"
710
+ ],
711
+ "additionalProperties": false
712
+ }
306
713
  },
307
714
  "assert_exit_code": {
308
715
  "description": "Assert the exit code captured from a command node output.",
@@ -315,7 +722,22 @@
315
722
  "intent": "Assert the exit code captured from a command node output"
316
723
  }
317
724
  }
318
- ]
725
+ ],
726
+ "schema": {
727
+ "type": "object",
728
+ "properties": {
729
+ "source": {
730
+ "type": "string"
731
+ },
732
+ "node": {
733
+ "type": "string"
734
+ },
735
+ "expected": {
736
+ "type": "number"
737
+ }
738
+ },
739
+ "additionalProperties": false
740
+ }
319
741
  },
320
742
  "assert_output": {
321
743
  "description": "Assert stdout or stderr captured from a command node output.",
@@ -329,10 +751,80 @@
329
751
  "intent": "Assert stdout or stderr captured from a command node output"
330
752
  }
331
753
  }
332
- ]
754
+ ],
755
+ "schema": {
756
+ "type": "object",
757
+ "properties": {
758
+ "source": {
759
+ "type": "string"
760
+ },
761
+ "node": {
762
+ "type": "string"
763
+ },
764
+ "stream": {
765
+ "type": "string"
766
+ },
767
+ "contains": {
768
+ "type": "string"
769
+ },
770
+ "match": {
771
+ "type": "string"
772
+ },
773
+ "assert": {
774
+ "type": "object",
775
+ "properties": {
776
+ "path": {
777
+ "type": "string"
778
+ },
779
+ "operator": {
780
+ "type": "string"
781
+ },
782
+ "value": {
783
+ "type": [
784
+ "string",
785
+ "number",
786
+ "integer",
787
+ "boolean",
788
+ "object",
789
+ "array"
790
+ ],
791
+ "items": {
792
+ "type": [
793
+ "string",
794
+ "number",
795
+ "integer",
796
+ "boolean",
797
+ "object"
798
+ ]
799
+ }
800
+ },
801
+ "all": {
802
+ "type": "array",
803
+ "items": {
804
+ "type": "object"
805
+ }
806
+ },
807
+ "any": {
808
+ "type": "array",
809
+ "items": {
810
+ "type": "object"
811
+ }
812
+ },
813
+ "none": {
814
+ "type": "array",
815
+ "items": {
816
+ "type": "object"
817
+ }
818
+ }
819
+ },
820
+ "additionalProperties": false
821
+ }
822
+ },
823
+ "additionalProperties": false
824
+ }
333
825
  },
334
826
  "app.hud": {
335
- "description": "Display minimal human-readable recipe progress. The intent must explain the current agent goal; debug metadata and flow/domain labels are hidden unless explicitly opted in.",
827
+ "description": "Display minimal human-readable recipe progress. The intent must explain the current agent goal; debug metadata and grouping labels are hidden unless explicitly opted in.",
336
828
  "examples": [
337
829
  {
338
830
  "description": "Default one-intent progress HUD",
@@ -351,6 +843,12 @@
351
843
  "schema": {
352
844
  "type": "object",
353
845
  "properties": {
846
+ "clear": {
847
+ "type": "boolean"
848
+ },
849
+ "title": {
850
+ "type": "string"
851
+ },
354
852
  "status": {
355
853
  "type": "string",
356
854
  "enum": [
@@ -359,454 +857,176 @@
359
857
  "fail"
360
858
  ]
361
859
  },
362
- "intent": {
860
+ "node_id": {
363
861
  "type": "string"
364
862
  },
365
- "progress": {
366
- "type": "object",
367
- "properties": {
368
- "current": {
369
- "type": "number"
370
- },
371
- "total": {
372
- "type": "number"
373
- }
374
- },
375
- "required": [
376
- "current",
377
- "total"
378
- ]
379
- },
380
- "detail": {
863
+ "nodeId": {
381
864
  "type": "string"
382
865
  },
383
- "error": {
866
+ "phase": {
384
867
  "type": "string"
385
868
  },
386
- "display": {
387
- "type": "object",
388
- "properties": {
389
- "showDetail": {
390
- "type": "boolean"
391
- },
392
- "showDebug": {
393
- "type": "boolean"
394
- },
395
- "showTitle": {
396
- "type": "boolean"
397
- },
398
- "layout": {
399
- "type": "string"
400
- },
401
- "position": {
402
- "type": "string"
403
- }
404
- }
405
- },
406
- "clear": {
407
- "type": "boolean"
408
- }
409
- }
410
- }
411
- },
412
- "ui.set_input": {
413
- "description": "Set text on a React Native TextInput by testID through the app control bridge.",
414
- "schema": {
415
- "type": "object",
416
- "properties": {
417
- "test_id": {
869
+ "action_name": {
418
870
  "type": "string"
419
871
  },
420
- "testID": {
872
+ "recipe_action": {
421
873
  "type": "string"
422
874
  },
423
- "value": {
424
- "type": "string"
425
- },
426
- "text": {
427
- "type": "string"
428
- },
429
- "next": {
430
- "type": "string"
431
- }
432
- },
433
- "additionalProperties": true
434
- },
435
- "examples": [
436
- {
437
- "node": {
438
- "action": "ui.set_input",
439
- "test_id": "network-selector-network-search-input",
440
- "value": "Sepolia",
441
- "intent": "Set text on a React Native TextInput by testID through the app control bridge"
442
- }
443
- }
444
- ]
445
- },
446
- "ui.key_press": {
447
- "description": "Send a trusted keyboard press to the currently focused UI element through the UI transport.",
448
- "schema": {
449
- "type": "object",
450
- "properties": {
451
- "key": {
452
- "type": "string"
453
- },
454
- "next": {
455
- "type": "string"
456
- }
457
- },
458
- "required": [
459
- "key"
460
- ],
461
- "additionalProperties": true
462
- },
463
- "examples": [
464
- {
465
- "node": {
466
- "action": "ui.key_press",
467
- "key": "Enter",
468
- "intent": "Send a trusted Enter key press to the focused UI element"
469
- }
470
- }
471
- ]
472
- },
473
- "app.status": {
474
- "description": "Report the adapter's static status — platform, project root, resolved checkout shape, and headless compatibility mode (no live route or account).",
475
- "examples": [
476
- {
477
- "node": {
478
- "action": "app.status",
479
- "intent": "Report the resolved checkout status"
480
- }
481
- }
482
- ]
483
- },
484
- "cdp.target": {
485
- "description": "Probe the platform debug transport — Chrome CDP for extension, the React Native debug bridge for mobile — and report whether it is reachable.",
486
- "examples": [
487
- {
488
- "node": {
489
- "action": "cdp.target",
490
- "require_reachable": true,
491
- "intent": "Confirm the mobile React Native debug bridge is reachable"
492
- }
493
- }
494
- ]
495
- },
496
- "metamask.wallet.fixture_status": {
497
- "description": "Report the on-disk wallet fixture status (accounts and password presence) without launching or mutating the app.",
498
- "examples": [
499
- {
500
- "description": "Minimal node",
501
- "node": {
502
- "action": "metamask.wallet.fixture_status",
503
- "intent": "Check wallet fixture status"
504
- }
505
- }
506
- ]
507
- },
508
- "metamask.wallet.setup": {
509
- "description": "Import the wallet fixture accounts and password so the app starts from a known signed-in state.",
510
- "examples": [
511
- {
512
- "description": "Minimal node",
513
- "node": {
514
- "action": "metamask.wallet.setup",
515
- "intent": "Prepare the wallet fixture for recipe execution"
516
- }
517
- }
518
- ]
519
- },
520
- "metamask.wallet.ensure_unlocked": {
521
- "description": "Unlock the wallet with the fixture password when it is currently locked.",
522
- "examples": [
523
- {
524
- "description": "Minimal node",
525
- "node": {
526
- "action": "metamask.wallet.ensure_unlocked",
527
- "intent": "Ensure the wallet is unlocked before proof steps"
528
- }
529
- }
530
- ]
531
- },
532
- "metamask.wallet.select_account": {
533
- "description": "Select a wallet account by address, id, or display name.",
534
- "examples": [
535
- {
536
- "description": "Select by deterministic fixture address",
537
- "node": {
538
- "action": "metamask.wallet.select_account",
539
- "address": "0x8dc623e964475d4d669da601fd15ea9125469003",
540
- "intent": "Select the requested wallet account"
541
- }
542
- },
543
- {
544
- "description": "Select by fixture display name",
545
- "node": {
546
- "action": "metamask.wallet.select_account",
547
- "name": "dev1",
548
- "intent": "Select the requested wallet account"
549
- }
550
- }
551
- ]
552
- },
553
- "metamask.wallet.list_accounts": {
554
- "description": "List redacted wallet accounts and identify the selected account without mutating the app.",
555
- "examples": [
556
- {
557
- "description": "List wallet accounts",
558
- "node": {
559
- "action": "metamask.wallet.list_accounts",
560
- "intent": "Discover the available wallet accounts"
561
- }
562
- }
563
- ]
564
- },
565
- "metamask.wallet.read_state": {
566
- "description": "Read the redacted wallet state (selected account, route, and device) from the running app.",
567
- "examples": [
568
- {
569
- "description": "Minimal node",
570
- "node": {
571
- "action": "metamask.wallet.read_state",
572
- "intent": "Read wallet state needed for the proof"
573
- }
574
- }
575
- ]
576
- },
577
- "metamask.perps.read_positions": {
578
- "description": "Read live Perps positions and return the selected subset.",
579
- "examples": [
580
- {
581
- "description": "Read selected live state",
582
- "node": {
583
- "action": "metamask.perps.read_positions",
584
- "market": "BTC",
585
- "intent": "Read Perps positions needed for the proof"
586
- }
587
- }
588
- ]
589
- },
590
- "metamask.perps.read_orders": {
591
- "description": "Read live Perps open orders and return the selected subset.",
592
- "examples": [
593
- {
594
- "description": "Read selected live state",
595
- "node": {
596
- "action": "metamask.perps.read_orders",
597
- "market": "BTC",
598
- "intent": "Read Perps open orders needed for the proof"
599
- }
600
- }
601
- ]
602
- },
603
- "metamask.perps.close_positions": {
604
- "description": "Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Close selected live Perps positions. Use selector/mode params to close matching symbols or all positions. The network field in examples is an authoring convention: only start_state/teardown_state enforce the network (ensureNetwork); the mutation handlers themselves do not read it.",
605
- "examples": [
606
- {
607
- "description": "Close every open position",
608
- "node": {
609
- "action": "metamask.perps.close_positions",
610
- "mode": "all",
611
- "intent": "Close selected Perps positions before continuing",
612
- "network": "testnet"
613
- }
614
- },
615
- {
616
- "description": "Close selected BTC/ETH positions",
617
- "node": {
618
- "action": "metamask.perps.close_positions",
619
- "markets": [
620
- "BTC",
621
- "ETH"
622
- ],
623
- "intent": "Close selected Perps positions before continuing",
624
- "network": "testnet"
625
- }
626
- }
627
- ]
628
- },
629
- "metamask.perps.close_orders": {
630
- "description": "Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Cancel selected live Perps open orders. Use selector/mode params to cancel matching symbols or all orders. The network field in examples is an authoring convention: only start_state/teardown_state enforce the network (ensureNetwork); the mutation handlers themselves do not read it.",
631
- "examples": [
632
- {
633
- "description": "Cancel every open order",
634
- "node": {
635
- "action": "metamask.perps.close_orders",
636
- "mode": "all",
637
- "intent": "Cancel selected Perps orders before continuing",
638
- "network": "testnet"
639
- }
640
- },
641
- {
642
- "description": "Cancel BTC orders only",
643
- "node": {
644
- "action": "metamask.perps.close_orders",
645
- "market": "BTC",
646
- "intent": "Cancel selected Perps orders before continuing",
647
- "network": "testnet"
648
- }
649
- }
650
- ]
651
- },
652
- "metamask.perps.place_order": {
653
- "description": "Place a Perps order through a supported app/API path. Default to testnet; mainnet order placement requires an explicit user request. The network field in examples is an authoring convention: only start_state/teardown_state enforce the network (ensureNetwork); the mutation handlers themselves do not read it.",
654
- "examples": [
655
- {
656
- "description": "Place BTC market order",
657
- "node": {
658
- "action": "metamask.perps.place_order",
659
- "market": "BTC",
660
- "side": "long",
661
- "notional": "10",
662
- "leverage": 2,
663
- "intent": "Place the requested Perps order",
664
- "network": "testnet"
875
+ "text": {
876
+ "type": "string"
877
+ },
878
+ "detail": {
879
+ "type": "string"
880
+ },
881
+ "error": {
882
+ "type": "string"
883
+ },
884
+ "display": {
885
+ "type": "object"
886
+ },
887
+ "progress": {
888
+ "type": "object"
665
889
  }
666
- }
667
- ]
890
+ },
891
+ "additionalProperties": false
892
+ }
668
893
  },
669
- "metamask.perps.assert_positions": {
670
- "description": "Assert selected live Perps positions are present or absent.",
671
- "examples": [
672
- {
673
- "description": "Assert BTC position is open",
674
- "node": {
675
- "action": "metamask.perps.assert_positions",
676
- "state": "open",
677
- "market": "BTC",
678
- "intent": "Assert the expected Perps position state"
894
+ "ui.set_input": {
895
+ "description": "Set text on a React Native TextInput by testID through the app control bridge.",
896
+ "schema": {
897
+ "type": "object",
898
+ "properties": {
899
+ "selector": {
900
+ "type": "string"
901
+ },
902
+ "test_id": {
903
+ "type": "string"
904
+ },
905
+ "testID": {
906
+ "type": "string"
907
+ },
908
+ "value": {
909
+ "type": "string"
910
+ },
911
+ "text": {
912
+ "type": "string"
913
+ },
914
+ "timeout_ms": {
915
+ "type": "number"
916
+ },
917
+ "settle": {
918
+ "type": "boolean"
679
919
  }
680
- }
681
- ]
682
- },
683
- "metamask.perps.assert_orders": {
684
- "description": "Assert selected live Perps open orders are present or absent.",
920
+ },
921
+ "additionalProperties": false
922
+ },
685
923
  "examples": [
686
924
  {
687
- "description": "Assert no BTC open orders",
688
925
  "node": {
689
- "action": "metamask.perps.assert_orders",
690
- "state": "none",
691
- "market": "BTC",
692
- "intent": "Assert the expected Perps open order state"
926
+ "action": "ui.set_input",
927
+ "test_id": "network-selector-network-search-input",
928
+ "value": "Sepolia",
929
+ "intent": "Set text on a React Native TextInput by testID through the app control bridge"
693
930
  }
694
931
  }
695
932
  ]
696
933
  },
697
- "metamask.perps.ensure_positions": {
698
- "description": "Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Higher-level wrapper that reads selected positions, closes or places when needed, then asserts final position state. The network field in examples is an authoring convention: only start_state/teardown_state enforce the network (ensureNetwork); the mutation handlers themselves do not read it.",
699
- "examples": [
700
- {
701
- "description": "Ensure no BTC position before proof window",
702
- "node": {
703
- "action": "metamask.perps.ensure_positions",
704
- "state": "none",
705
- "market": "BTC",
706
- "intent": "Converge Perps positions to the requested state",
707
- "network": "testnet"
934
+ "ui.key_press": {
935
+ "description": "Send a trusted keyboard press to the currently focused UI element through the UI transport.",
936
+ "schema": {
937
+ "type": "object",
938
+ "properties": {
939
+ "key": {
940
+ "type": "string"
941
+ },
942
+ "timeout_ms": {
943
+ "type": "number"
944
+ },
945
+ "settle": {
946
+ "type": "boolean"
708
947
  }
709
948
  },
949
+ "required": [
950
+ "key"
951
+ ],
952
+ "additionalProperties": false
953
+ },
954
+ "examples": [
710
955
  {
711
- "description": "Ensure any existing positions are closed",
712
956
  "node": {
713
- "action": "metamask.perps.ensure_positions",
714
- "state": "none",
715
- "mode": "all",
716
- "intent": "Converge Perps positions to the requested state",
717
- "network": "testnet"
957
+ "action": "ui.key_press",
958
+ "key": "Enter",
959
+ "intent": "Send a trusted Enter key press to the focused UI element"
718
960
  }
719
961
  }
720
962
  ]
721
963
  },
722
- "metamask.perps.ensure_orders": {
723
- "description": "Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Higher-level wrapper that reads selected orders, cancels when needed, then asserts final order state. The network field in examples is an authoring convention: only start_state/teardown_state enforce the network (ensureNetwork); the mutation handlers themselves do not read it.",
964
+ "app.status": {
965
+ "description": "Report the adapter's static status platform, project root, resolved checkout shape, and headless compatibility mode (no live route or account).",
724
966
  "examples": [
725
967
  {
726
- "description": "Ensure no open orders before proof window",
727
968
  "node": {
728
- "action": "metamask.perps.ensure_orders",
729
- "state": "none",
730
- "mode": "all",
731
- "intent": "Converge Perps orders to the requested state",
732
- "network": "testnet"
969
+ "action": "app.status",
970
+ "intent": "Report the resolved checkout status"
733
971
  }
734
972
  }
735
- ]
973
+ ],
974
+ "schema": {
975
+ "type": "object",
976
+ "properties": {},
977
+ "additionalProperties": false
978
+ }
736
979
  },
737
- "metamask.perps.start_state": {
738
- "description": "Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Recommended configurable Perps start state that first restores the fixture-backed unlocked wallet, then composes reusable domain operations before the proof window.",
980
+ "cdp.target": {
981
+ "description": "Probe the platform debug transport Chrome CDP for extension, the React Native debug bridge for mobile and report whether it is reachable.",
739
982
  "examples": [
740
983
  {
741
- "description": "Clean BTC testnet market state",
742
- "node": {
743
- "action": "metamask.perps.start_state",
744
- "profile": "clean_market_testnet",
745
- "market": "BTC",
746
- "positions": {
747
- "state": "none"
748
- },
749
- "orders": {
750
- "state": "none"
751
- },
752
- "intent": "Prepare the Perps start state for the proof",
753
- "network": "testnet"
754
- }
755
- },
756
- {
757
- "description": "Start with an open ETH long position",
758
984
  "node": {
759
- "action": "metamask.perps.start_state",
760
- "profile": "open_position_testnet",
761
- "market": "ETH",
762
- "side": "long",
763
- "positions": {
764
- "state": "open",
765
- "notional": "10",
766
- "leverage": 2
767
- },
768
- "intent": "Prepare the Perps start state for the proof",
769
- "network": "testnet"
985
+ "action": "cdp.target",
986
+ "require_reachable": true,
987
+ "intent": "Confirm the mobile React Native debug bridge is reachable"
770
988
  }
771
989
  }
772
- ]
773
- },
774
- "metamask.perps.teardown_state": {
775
- "description": "Recommended configurable Perps teardown state that restores the selected environment after proof.",
776
- "examples": [
777
- {
778
- "description": "Return BTC test account to clean state",
779
- "node": {
780
- "action": "metamask.perps.teardown_state",
781
- "market": "BTC",
782
- "positions": {
783
- "state": "none"
784
- },
785
- "orders": {
786
- "state": "none"
787
- },
788
- "page": "home",
789
- "intent": "Restore Perps state after the proof"
990
+ ],
991
+ "schema": {
992
+ "type": "object",
993
+ "properties": {
994
+ "cdp_port": {
995
+ "type": "number"
996
+ },
997
+ "metro_port": {
998
+ "type": "number"
999
+ },
1000
+ "watcher_port": {
1001
+ "type": "number"
1002
+ },
1003
+ "probe_timeout_ms": {
1004
+ "type": "number"
1005
+ },
1006
+ "cdp_timeout_ms": {
1007
+ "type": "number"
1008
+ },
1009
+ "timeout_ms": {
1010
+ "type": "number"
1011
+ },
1012
+ "require_reachable": {
1013
+ "type": "boolean"
1014
+ },
1015
+ "required": {
1016
+ "type": "boolean"
790
1017
  }
791
1018
  },
792
- {
793
- "description": "Teardown only state created by selected market",
794
- "node": {
795
- "action": "metamask.perps.teardown_state",
796
- "market": "BTC",
797
- "intent": "Restore Perps state after the proof"
798
- }
799
- }
800
- ]
1019
+ "additionalProperties": false
1020
+ }
801
1021
  },
802
1022
  "call": {
803
- "description": "Invoke a named sub-recipe flow by ref and run its steps inline.",
1023
+ "description": "Invoke a named recipe by ref and run its steps inline.",
804
1024
  "examples": [
805
1025
  {
806
1026
  "node": {
807
1027
  "action": "call",
808
1028
  "ref": "mydev.open_perps_setup",
809
- "intent": "Run a personal Perps setup flow segment"
1029
+ "intent": "Run a personal Perps setup recipe"
810
1030
  }
811
1031
  }
812
1032
  ]
@@ -819,14 +1039,8 @@
819
1039
  "description": "Report the on-disk wallet fixture status (accounts and password presence) without launching or mutating the app.",
820
1040
  "schema": {
821
1041
  "type": "object",
822
- "properties": {
823
- "action": {
824
- "const": "metamask.wallet.fixture_status"
825
- }
826
- },
827
- "required": [
828
- "action"
829
- ]
1042
+ "properties": {},
1043
+ "additionalProperties": false
830
1044
  },
831
1045
  "examples": [
832
1046
  {
@@ -838,7 +1052,8 @@
838
1052
  }
839
1053
  ],
840
1054
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
841
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1055
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1056
+ "execution_capabilities": []
842
1057
  },
843
1058
  {
844
1059
  "name": "metamask.wallet.setup",
@@ -846,14 +1061,8 @@
846
1061
  "description": "Import the wallet fixture accounts and password so the app starts from a known signed-in state.",
847
1062
  "schema": {
848
1063
  "type": "object",
849
- "properties": {
850
- "action": {
851
- "const": "metamask.wallet.setup"
852
- }
853
- },
854
- "required": [
855
- "action"
856
- ]
1064
+ "properties": {},
1065
+ "additionalProperties": false
857
1066
  },
858
1067
  "examples": [
859
1068
  {
@@ -865,7 +1074,10 @@
865
1074
  }
866
1075
  ],
867
1076
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
868
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1077
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1078
+ "execution_capabilities": [
1079
+ "app-mutation"
1080
+ ]
869
1081
  },
870
1082
  {
871
1083
  "name": "metamask.wallet.ensure_unlocked",
@@ -874,13 +1086,14 @@
874
1086
  "schema": {
875
1087
  "type": "object",
876
1088
  "properties": {
877
- "action": {
878
- "const": "metamask.wallet.ensure_unlocked"
1089
+ "target_timeout_ms": {
1090
+ "type": "number"
1091
+ },
1092
+ "unlock_timeout_ms": {
1093
+ "type": "number"
879
1094
  }
880
1095
  },
881
- "required": [
882
- "action"
883
- ]
1096
+ "additionalProperties": false
884
1097
  },
885
1098
  "examples": [
886
1099
  {
@@ -892,7 +1105,10 @@
892
1105
  }
893
1106
  ],
894
1107
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
895
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1108
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1109
+ "execution_capabilities": [
1110
+ "app-mutation"
1111
+ ]
896
1112
  },
897
1113
  {
898
1114
  "name": "metamask.wallet.select_account",
@@ -901,9 +1117,6 @@
901
1117
  "schema": {
902
1118
  "type": "object",
903
1119
  "properties": {
904
- "action": {
905
- "const": "metamask.wallet.select_account"
906
- },
907
1120
  "address": {
908
1121
  "type": "string",
909
1122
  "description": "EVM address of the account to select."
@@ -917,26 +1130,7 @@
917
1130
  "description": "Display name of the account to select."
918
1131
  }
919
1132
  },
920
- "required": [
921
- "action"
922
- ],
923
- "anyOf": [
924
- {
925
- "required": [
926
- "address"
927
- ]
928
- },
929
- {
930
- "required": [
931
- "id"
932
- ]
933
- },
934
- {
935
- "required": [
936
- "name"
937
- ]
938
- }
939
- ]
1133
+ "additionalProperties": false
940
1134
  },
941
1135
  "examples": [
942
1136
  {
@@ -957,7 +1151,10 @@
957
1151
  }
958
1152
  ],
959
1153
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
960
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1154
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1155
+ "execution_capabilities": [
1156
+ "app-mutation"
1157
+ ]
961
1158
  },
962
1159
  {
963
1160
  "name": "metamask.wallet.list_accounts",
@@ -966,11 +1163,9 @@
966
1163
  "schema": {
967
1164
  "type": "object",
968
1165
  "properties": {
969
- "action": {
970
- "const": "metamask.wallet.list_accounts"
971
- },
972
1166
  "scope": {
973
1167
  "type": "string",
1168
+ "default": "named",
974
1169
  "enum": [
975
1170
  "selected",
976
1171
  "named",
@@ -980,9 +1175,7 @@
980
1175
  "description": "Account scope. Defaults to named human-visible accounts; use selected for the active account, evm for EVM accounts, or all for the full inventory."
981
1176
  }
982
1177
  },
983
- "required": [
984
- "action"
985
- ]
1178
+ "additionalProperties": false
986
1179
  },
987
1180
  "examples": [
988
1181
  {
@@ -994,7 +1187,8 @@
994
1187
  }
995
1188
  ],
996
1189
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
997
- "safety_notes": "Read-only account metadata; must never expose keys, mnemonics, vault data, or credentials."
1190
+ "safety_notes": "Read-only account metadata; must never expose keys, mnemonics, vault data, or credentials.",
1191
+ "execution_capabilities": []
998
1192
  },
999
1193
  {
1000
1194
  "name": "metamask.wallet.read_state",
@@ -1002,14 +1196,8 @@
1002
1196
  "description": "Read the redacted wallet state (selected account, route, and device) from the running app.",
1003
1197
  "schema": {
1004
1198
  "type": "object",
1005
- "properties": {
1006
- "action": {
1007
- "const": "metamask.wallet.read_state"
1008
- }
1009
- },
1010
- "required": [
1011
- "action"
1012
- ]
1199
+ "properties": {},
1200
+ "additionalProperties": false
1013
1201
  },
1014
1202
  "examples": [
1015
1203
  {
@@ -1021,18 +1209,16 @@
1021
1209
  }
1022
1210
  ],
1023
1211
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1024
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1212
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1213
+ "execution_capabilities": []
1025
1214
  },
1026
1215
  {
1027
1216
  "name": "metamask.perps.read_positions",
1028
1217
  "owner": "metamask",
1029
- "description": "mobile Read live Perps positions and return the selected subset.",
1218
+ "description": "mobile Read live Perps positions; without a selector, return all live positions.",
1030
1219
  "schema": {
1031
1220
  "type": "object",
1032
1221
  "properties": {
1033
- "action": {
1034
- "const": "metamask.perps.read_positions"
1035
- },
1036
1222
  "market": {
1037
1223
  "type": "string",
1038
1224
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1065,7 +1251,41 @@
1065
1251
  },
1066
1252
  "selector": {
1067
1253
  "type": "object",
1068
- "description": "Optional structured selector with mode, symbols/markets, and side."
1254
+ "properties": {
1255
+ "mode": {
1256
+ "type": "string",
1257
+ "enum": [
1258
+ "matching",
1259
+ "all"
1260
+ ]
1261
+ },
1262
+ "market": {
1263
+ "type": "string"
1264
+ },
1265
+ "symbol": {
1266
+ "type": "string"
1267
+ },
1268
+ "markets": {
1269
+ "type": "array",
1270
+ "items": {
1271
+ "type": "string"
1272
+ }
1273
+ },
1274
+ "symbols": {
1275
+ "type": "array",
1276
+ "items": {
1277
+ "type": "string"
1278
+ }
1279
+ },
1280
+ "side": {
1281
+ "type": "string",
1282
+ "enum": [
1283
+ "long",
1284
+ "short"
1285
+ ]
1286
+ }
1287
+ },
1288
+ "additionalProperties": false
1069
1289
  },
1070
1290
  "side": {
1071
1291
  "type": "string",
@@ -1079,9 +1299,7 @@
1079
1299
  "type": "number"
1080
1300
  }
1081
1301
  },
1082
- "required": [
1083
- "action"
1084
- ]
1302
+ "additionalProperties": false
1085
1303
  },
1086
1304
  "examples": [
1087
1305
  {
@@ -1094,18 +1312,16 @@
1094
1312
  }
1095
1313
  ],
1096
1314
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1097
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1315
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1316
+ "execution_capabilities": []
1098
1317
  },
1099
1318
  {
1100
1319
  "name": "metamask.perps.read_orders",
1101
1320
  "owner": "metamask",
1102
- "description": "mobile Read live Perps open orders and return the selected subset.",
1321
+ "description": "mobile Read live Perps open orders; without a selector, return all live orders.",
1103
1322
  "schema": {
1104
1323
  "type": "object",
1105
1324
  "properties": {
1106
- "action": {
1107
- "const": "metamask.perps.read_orders"
1108
- },
1109
1325
  "market": {
1110
1326
  "type": "string",
1111
1327
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1138,7 +1354,41 @@
1138
1354
  },
1139
1355
  "selector": {
1140
1356
  "type": "object",
1141
- "description": "Optional structured selector with mode, symbols/markets, and side."
1357
+ "properties": {
1358
+ "mode": {
1359
+ "type": "string",
1360
+ "enum": [
1361
+ "matching",
1362
+ "all"
1363
+ ]
1364
+ },
1365
+ "market": {
1366
+ "type": "string"
1367
+ },
1368
+ "symbol": {
1369
+ "type": "string"
1370
+ },
1371
+ "markets": {
1372
+ "type": "array",
1373
+ "items": {
1374
+ "type": "string"
1375
+ }
1376
+ },
1377
+ "symbols": {
1378
+ "type": "array",
1379
+ "items": {
1380
+ "type": "string"
1381
+ }
1382
+ },
1383
+ "side": {
1384
+ "type": "string",
1385
+ "enum": [
1386
+ "long",
1387
+ "short"
1388
+ ]
1389
+ }
1390
+ },
1391
+ "additionalProperties": false
1142
1392
  },
1143
1393
  "side": {
1144
1394
  "type": "string",
@@ -1152,9 +1402,7 @@
1152
1402
  "type": "number"
1153
1403
  }
1154
1404
  },
1155
- "required": [
1156
- "action"
1157
- ]
1405
+ "additionalProperties": false
1158
1406
  },
1159
1407
  "examples": [
1160
1408
  {
@@ -1167,7 +1415,8 @@
1167
1415
  }
1168
1416
  ],
1169
1417
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1170
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1418
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1419
+ "execution_capabilities": []
1171
1420
  },
1172
1421
  {
1173
1422
  "name": "metamask.perps.close_positions",
@@ -1176,9 +1425,6 @@
1176
1425
  "schema": {
1177
1426
  "type": "object",
1178
1427
  "properties": {
1179
- "action": {
1180
- "const": "metamask.perps.close_positions"
1181
- },
1182
1428
  "market": {
1183
1429
  "type": "string",
1184
1430
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1211,7 +1457,41 @@
1211
1457
  },
1212
1458
  "selector": {
1213
1459
  "type": "object",
1214
- "description": "Optional structured selector with mode, symbols/markets, and side."
1460
+ "properties": {
1461
+ "mode": {
1462
+ "type": "string",
1463
+ "enum": [
1464
+ "matching",
1465
+ "all"
1466
+ ]
1467
+ },
1468
+ "market": {
1469
+ "type": "string"
1470
+ },
1471
+ "symbol": {
1472
+ "type": "string"
1473
+ },
1474
+ "markets": {
1475
+ "type": "array",
1476
+ "items": {
1477
+ "type": "string"
1478
+ }
1479
+ },
1480
+ "symbols": {
1481
+ "type": "array",
1482
+ "items": {
1483
+ "type": "string"
1484
+ }
1485
+ },
1486
+ "side": {
1487
+ "type": "string",
1488
+ "enum": [
1489
+ "long",
1490
+ "short"
1491
+ ]
1492
+ }
1493
+ },
1494
+ "additionalProperties": false
1215
1495
  },
1216
1496
  "side": {
1217
1497
  "type": "string",
@@ -1225,9 +1505,7 @@
1225
1505
  "type": "number"
1226
1506
  }
1227
1507
  },
1228
- "required": [
1229
- "action"
1230
- ]
1508
+ "additionalProperties": false
1231
1509
  },
1232
1510
  "examples": [
1233
1511
  {
@@ -1235,8 +1513,7 @@
1235
1513
  "node": {
1236
1514
  "action": "metamask.perps.close_positions",
1237
1515
  "mode": "all",
1238
- "intent": "Close selected Perps positions before continuing",
1239
- "network": "testnet"
1516
+ "intent": "Close selected Perps positions before continuing"
1240
1517
  }
1241
1518
  },
1242
1519
  {
@@ -1247,13 +1524,16 @@
1247
1524
  "BTC",
1248
1525
  "ETH"
1249
1526
  ],
1250
- "intent": "Close selected Perps positions before continuing",
1251
- "network": "testnet"
1527
+ "intent": "Close selected Perps positions before continuing"
1252
1528
  }
1253
1529
  }
1254
1530
  ],
1255
1531
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1256
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1532
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1533
+ "execution_capabilities": [
1534
+ "app-mutation",
1535
+ "external-mutation"
1536
+ ]
1257
1537
  },
1258
1538
  {
1259
1539
  "name": "metamask.perps.close_orders",
@@ -1262,9 +1542,6 @@
1262
1542
  "schema": {
1263
1543
  "type": "object",
1264
1544
  "properties": {
1265
- "action": {
1266
- "const": "metamask.perps.close_orders"
1267
- },
1268
1545
  "market": {
1269
1546
  "type": "string",
1270
1547
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1297,7 +1574,41 @@
1297
1574
  },
1298
1575
  "selector": {
1299
1576
  "type": "object",
1300
- "description": "Optional structured selector with mode, symbols/markets, and side."
1577
+ "properties": {
1578
+ "mode": {
1579
+ "type": "string",
1580
+ "enum": [
1581
+ "matching",
1582
+ "all"
1583
+ ]
1584
+ },
1585
+ "market": {
1586
+ "type": "string"
1587
+ },
1588
+ "symbol": {
1589
+ "type": "string"
1590
+ },
1591
+ "markets": {
1592
+ "type": "array",
1593
+ "items": {
1594
+ "type": "string"
1595
+ }
1596
+ },
1597
+ "symbols": {
1598
+ "type": "array",
1599
+ "items": {
1600
+ "type": "string"
1601
+ }
1602
+ },
1603
+ "side": {
1604
+ "type": "string",
1605
+ "enum": [
1606
+ "long",
1607
+ "short"
1608
+ ]
1609
+ }
1610
+ },
1611
+ "additionalProperties": false
1301
1612
  },
1302
1613
  "side": {
1303
1614
  "type": "string",
@@ -1311,9 +1622,7 @@
1311
1622
  "type": "number"
1312
1623
  }
1313
1624
  },
1314
- "required": [
1315
- "action"
1316
- ]
1625
+ "additionalProperties": false
1317
1626
  },
1318
1627
  "examples": [
1319
1628
  {
@@ -1321,8 +1630,7 @@
1321
1630
  "node": {
1322
1631
  "action": "metamask.perps.close_orders",
1323
1632
  "mode": "all",
1324
- "intent": "Cancel selected Perps orders before continuing",
1325
- "network": "testnet"
1633
+ "intent": "Cancel selected Perps orders before continuing"
1326
1634
  }
1327
1635
  },
1328
1636
  {
@@ -1330,13 +1638,16 @@
1330
1638
  "node": {
1331
1639
  "action": "metamask.perps.close_orders",
1332
1640
  "market": "BTC",
1333
- "intent": "Cancel selected Perps orders before continuing",
1334
- "network": "testnet"
1641
+ "intent": "Cancel selected Perps orders before continuing"
1335
1642
  }
1336
1643
  }
1337
1644
  ],
1338
1645
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1339
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1646
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1647
+ "execution_capabilities": [
1648
+ "app-mutation",
1649
+ "external-mutation"
1650
+ ]
1340
1651
  },
1341
1652
  {
1342
1653
  "name": "metamask.perps.place_order",
@@ -1345,9 +1656,6 @@
1345
1656
  "schema": {
1346
1657
  "type": "object",
1347
1658
  "properties": {
1348
- "action": {
1349
- "const": "metamask.perps.place_order"
1350
- },
1351
1659
  "market": {
1352
1660
  "type": "string",
1353
1661
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1380,7 +1688,41 @@
1380
1688
  },
1381
1689
  "selector": {
1382
1690
  "type": "object",
1383
- "description": "Optional structured selector with mode, symbols/markets, and side."
1691
+ "properties": {
1692
+ "mode": {
1693
+ "type": "string",
1694
+ "enum": [
1695
+ "matching",
1696
+ "all"
1697
+ ]
1698
+ },
1699
+ "market": {
1700
+ "type": "string"
1701
+ },
1702
+ "symbol": {
1703
+ "type": "string"
1704
+ },
1705
+ "markets": {
1706
+ "type": "array",
1707
+ "items": {
1708
+ "type": "string"
1709
+ }
1710
+ },
1711
+ "symbols": {
1712
+ "type": "array",
1713
+ "items": {
1714
+ "type": "string"
1715
+ }
1716
+ },
1717
+ "side": {
1718
+ "type": "string",
1719
+ "enum": [
1720
+ "long",
1721
+ "short"
1722
+ ]
1723
+ }
1724
+ },
1725
+ "additionalProperties": false
1384
1726
  },
1385
1727
  "side": {
1386
1728
  "type": "string",
@@ -1406,12 +1748,11 @@
1406
1748
  "description": "Base asset size when supported."
1407
1749
  },
1408
1750
  "leverage": {
1409
- "type": "number"
1751
+ "type": "number",
1752
+ "default": 3
1410
1753
  }
1411
1754
  },
1412
- "required": [
1413
- "action"
1414
- ]
1755
+ "additionalProperties": false
1415
1756
  },
1416
1757
  "examples": [
1417
1758
  {
@@ -1422,24 +1763,24 @@
1422
1763
  "side": "long",
1423
1764
  "notional": "10",
1424
1765
  "leverage": 2,
1425
- "intent": "Place the requested Perps order",
1426
- "network": "testnet"
1766
+ "intent": "Place the requested Perps order"
1427
1767
  }
1428
1768
  }
1429
1769
  ],
1430
1770
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1431
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1771
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1772
+ "execution_capabilities": [
1773
+ "app-mutation",
1774
+ "external-mutation"
1775
+ ]
1432
1776
  },
1433
1777
  {
1434
1778
  "name": "metamask.perps.assert_positions",
1435
1779
  "owner": "metamask",
1436
- "description": "mobile Assert selected live Perps positions are present or absent.",
1780
+ "description": "mobile Assert live Perps positions are present or absent for an explicit market selection or mode=all.",
1437
1781
  "schema": {
1438
1782
  "type": "object",
1439
1783
  "properties": {
1440
- "action": {
1441
- "const": "metamask.perps.assert_positions"
1442
- },
1443
1784
  "market": {
1444
1785
  "type": "string",
1445
1786
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1472,7 +1813,41 @@
1472
1813
  },
1473
1814
  "selector": {
1474
1815
  "type": "object",
1475
- "description": "Optional structured selector with mode, symbols/markets, and side."
1816
+ "properties": {
1817
+ "mode": {
1818
+ "type": "string",
1819
+ "enum": [
1820
+ "matching",
1821
+ "all"
1822
+ ]
1823
+ },
1824
+ "market": {
1825
+ "type": "string"
1826
+ },
1827
+ "symbol": {
1828
+ "type": "string"
1829
+ },
1830
+ "markets": {
1831
+ "type": "array",
1832
+ "items": {
1833
+ "type": "string"
1834
+ }
1835
+ },
1836
+ "symbols": {
1837
+ "type": "array",
1838
+ "items": {
1839
+ "type": "string"
1840
+ }
1841
+ },
1842
+ "side": {
1843
+ "type": "string",
1844
+ "enum": [
1845
+ "long",
1846
+ "short"
1847
+ ]
1848
+ }
1849
+ },
1850
+ "additionalProperties": false
1476
1851
  },
1477
1852
  "side": {
1478
1853
  "type": "string",
@@ -1498,8 +1873,9 @@
1498
1873
  }
1499
1874
  },
1500
1875
  "required": [
1501
- "action"
1502
- ]
1876
+ "state"
1877
+ ],
1878
+ "additionalProperties": false
1503
1879
  },
1504
1880
  "examples": [
1505
1881
  {
@@ -1513,18 +1889,16 @@
1513
1889
  }
1514
1890
  ],
1515
1891
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1516
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1892
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1893
+ "execution_capabilities": []
1517
1894
  },
1518
1895
  {
1519
1896
  "name": "metamask.perps.assert_orders",
1520
1897
  "owner": "metamask",
1521
- "description": "mobile Assert selected live Perps open orders are present or absent.",
1898
+ "description": "mobile Assert live Perps open orders are present or absent for an explicit market selection or mode=all.",
1522
1899
  "schema": {
1523
1900
  "type": "object",
1524
1901
  "properties": {
1525
- "action": {
1526
- "const": "metamask.perps.assert_orders"
1527
- },
1528
1902
  "market": {
1529
1903
  "type": "string",
1530
1904
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1557,7 +1931,41 @@
1557
1931
  },
1558
1932
  "selector": {
1559
1933
  "type": "object",
1560
- "description": "Optional structured selector with mode, symbols/markets, and side."
1934
+ "properties": {
1935
+ "mode": {
1936
+ "type": "string",
1937
+ "enum": [
1938
+ "matching",
1939
+ "all"
1940
+ ]
1941
+ },
1942
+ "market": {
1943
+ "type": "string"
1944
+ },
1945
+ "symbol": {
1946
+ "type": "string"
1947
+ },
1948
+ "markets": {
1949
+ "type": "array",
1950
+ "items": {
1951
+ "type": "string"
1952
+ }
1953
+ },
1954
+ "symbols": {
1955
+ "type": "array",
1956
+ "items": {
1957
+ "type": "string"
1958
+ }
1959
+ },
1960
+ "side": {
1961
+ "type": "string",
1962
+ "enum": [
1963
+ "long",
1964
+ "short"
1965
+ ]
1966
+ }
1967
+ },
1968
+ "additionalProperties": false
1561
1969
  },
1562
1970
  "side": {
1563
1971
  "type": "string",
@@ -1583,8 +1991,9 @@
1583
1991
  }
1584
1992
  },
1585
1993
  "required": [
1586
- "action"
1587
- ]
1994
+ "state"
1995
+ ],
1996
+ "additionalProperties": false
1588
1997
  },
1589
1998
  "examples": [
1590
1999
  {
@@ -1598,7 +2007,8 @@
1598
2007
  }
1599
2008
  ],
1600
2009
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1601
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
2010
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
2011
+ "execution_capabilities": []
1602
2012
  },
1603
2013
  {
1604
2014
  "name": "metamask.perps.ensure_positions",
@@ -1607,9 +2017,6 @@
1607
2017
  "schema": {
1608
2018
  "type": "object",
1609
2019
  "properties": {
1610
- "action": {
1611
- "const": "metamask.perps.ensure_positions"
1612
- },
1613
2020
  "market": {
1614
2021
  "type": "string",
1615
2022
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1642,7 +2049,41 @@
1642
2049
  },
1643
2050
  "selector": {
1644
2051
  "type": "object",
1645
- "description": "Optional structured selector with mode, symbols/markets, and side."
2052
+ "properties": {
2053
+ "mode": {
2054
+ "type": "string",
2055
+ "enum": [
2056
+ "matching",
2057
+ "all"
2058
+ ]
2059
+ },
2060
+ "market": {
2061
+ "type": "string"
2062
+ },
2063
+ "symbol": {
2064
+ "type": "string"
2065
+ },
2066
+ "markets": {
2067
+ "type": "array",
2068
+ "items": {
2069
+ "type": "string"
2070
+ }
2071
+ },
2072
+ "symbols": {
2073
+ "type": "array",
2074
+ "items": {
2075
+ "type": "string"
2076
+ }
2077
+ },
2078
+ "side": {
2079
+ "type": "string",
2080
+ "enum": [
2081
+ "long",
2082
+ "short"
2083
+ ]
2084
+ }
2085
+ },
2086
+ "additionalProperties": false
1646
2087
  },
1647
2088
  "side": {
1648
2089
  "type": "string",
@@ -1668,8 +2109,9 @@
1668
2109
  }
1669
2110
  },
1670
2111
  "required": [
1671
- "action"
1672
- ]
2112
+ "state"
2113
+ ],
2114
+ "additionalProperties": false
1673
2115
  },
1674
2116
  "examples": [
1675
2117
  {
@@ -1678,8 +2120,7 @@
1678
2120
  "action": "metamask.perps.ensure_positions",
1679
2121
  "state": "none",
1680
2122
  "market": "BTC",
1681
- "intent": "Converge Perps positions to the requested state",
1682
- "network": "testnet"
2123
+ "intent": "Converge Perps positions to the requested state"
1683
2124
  }
1684
2125
  },
1685
2126
  {
@@ -1688,13 +2129,16 @@
1688
2129
  "action": "metamask.perps.ensure_positions",
1689
2130
  "state": "none",
1690
2131
  "mode": "all",
1691
- "intent": "Converge Perps positions to the requested state",
1692
- "network": "testnet"
2132
+ "intent": "Converge Perps positions to the requested state"
1693
2133
  }
1694
2134
  }
1695
2135
  ],
1696
2136
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1697
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
2137
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
2138
+ "execution_capabilities": [
2139
+ "app-mutation",
2140
+ "external-mutation"
2141
+ ]
1698
2142
  },
1699
2143
  {
1700
2144
  "name": "metamask.perps.ensure_orders",
@@ -1703,9 +2147,6 @@
1703
2147
  "schema": {
1704
2148
  "type": "object",
1705
2149
  "properties": {
1706
- "action": {
1707
- "const": "metamask.perps.ensure_orders"
1708
- },
1709
2150
  "market": {
1710
2151
  "type": "string",
1711
2152
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1736,141 +2177,411 @@
1736
2177
  ],
1737
2178
  "description": "matching selects requested symbols; all selects every live item returned by the product."
1738
2179
  },
1739
- "selector": {
2180
+ "selector": {
2181
+ "type": "object",
2182
+ "properties": {
2183
+ "mode": {
2184
+ "type": "string",
2185
+ "enum": [
2186
+ "matching",
2187
+ "all"
2188
+ ]
2189
+ },
2190
+ "market": {
2191
+ "type": "string"
2192
+ },
2193
+ "symbol": {
2194
+ "type": "string"
2195
+ },
2196
+ "markets": {
2197
+ "type": "array",
2198
+ "items": {
2199
+ "type": "string"
2200
+ }
2201
+ },
2202
+ "symbols": {
2203
+ "type": "array",
2204
+ "items": {
2205
+ "type": "string"
2206
+ }
2207
+ },
2208
+ "side": {
2209
+ "type": "string",
2210
+ "enum": [
2211
+ "long",
2212
+ "short"
2213
+ ]
2214
+ }
2215
+ },
2216
+ "additionalProperties": false
2217
+ },
2218
+ "side": {
2219
+ "type": "string",
2220
+ "enum": [
2221
+ "long",
2222
+ "short"
2223
+ ],
2224
+ "description": "Optional position/order side filter."
2225
+ },
2226
+ "timeout_ms": {
2227
+ "type": "number"
2228
+ },
2229
+ "state": {
2230
+ "type": "string",
2231
+ "enum": [
2232
+ "none",
2233
+ "absent",
2234
+ "closed",
2235
+ "open",
2236
+ "present"
2237
+ ],
2238
+ "description": "Desired/expected selected collection state."
2239
+ }
2240
+ },
2241
+ "required": [
2242
+ "state"
2243
+ ],
2244
+ "additionalProperties": false
2245
+ },
2246
+ "examples": [
2247
+ {
2248
+ "description": "Ensure no open orders before proof window",
2249
+ "node": {
2250
+ "action": "metamask.perps.ensure_orders",
2251
+ "state": "none",
2252
+ "mode": "all",
2253
+ "intent": "Converge Perps orders to the requested state"
2254
+ }
2255
+ }
2256
+ ],
2257
+ "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
2258
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
2259
+ "execution_capabilities": [
2260
+ "app-mutation",
2261
+ "external-mutation"
2262
+ ]
2263
+ },
2264
+ {
2265
+ "name": "metamask.perps.start_state",
2266
+ "owner": "metamask",
2267
+ "description": "mobile Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Recommended configurable Perps start state that first restores the fixture-backed unlocked wallet, then composes reusable domain operations before the proof window.",
2268
+ "schema": {
2269
+ "type": "object",
2270
+ "properties": {
2271
+ "profile": {
2272
+ "type": "string",
2273
+ "description": "Optional profile such as clean_market_testnet, open_position_testnet, open_order_testnet, or provider_mainnet_readonly."
2274
+ },
2275
+ "account": {
2276
+ "type": "string"
2277
+ },
2278
+ "provider": {
2279
+ "type": "string",
2280
+ "description": "Requested Perps provider, e.g. hyperliquid."
2281
+ },
2282
+ "network": {
2283
+ "type": "string",
2284
+ "enum": [
2285
+ "testnet",
2286
+ "mainnet"
2287
+ ]
2288
+ },
2289
+ "market": {
2290
+ "type": "string"
2291
+ },
2292
+ "symbol": {
2293
+ "type": "string"
2294
+ },
2295
+ "markets": {
2296
+ "type": "array",
2297
+ "items": {
2298
+ "type": "string"
2299
+ }
2300
+ },
2301
+ "symbols": {
2302
+ "type": "array",
2303
+ "items": {
2304
+ "type": "string"
2305
+ }
2306
+ },
2307
+ "page": {
2308
+ "description": "Initial Perps page target; omit it to use the profile or market default.",
2309
+ "type": "string",
2310
+ "enum": [
2311
+ "home",
2312
+ "perps",
2313
+ "perps_home",
2314
+ "market",
2315
+ "market_details"
2316
+ ]
2317
+ },
2318
+ "positions": {
2319
+ "type": [
2320
+ "object",
2321
+ "boolean"
2322
+ ],
2323
+ "properties": {
2324
+ "mode": {
2325
+ "type": "string",
2326
+ "enum": [
2327
+ "matching",
2328
+ "all"
2329
+ ]
2330
+ },
2331
+ "market": {
2332
+ "type": "string"
2333
+ },
2334
+ "symbol": {
2335
+ "type": "string"
2336
+ },
2337
+ "markets": {
2338
+ "type": "array",
2339
+ "items": {
2340
+ "type": "string"
2341
+ }
2342
+ },
2343
+ "symbols": {
2344
+ "type": "array",
2345
+ "items": {
2346
+ "type": "string"
2347
+ }
2348
+ },
2349
+ "side": {
2350
+ "type": "string",
2351
+ "enum": [
2352
+ "long",
2353
+ "short"
2354
+ ]
2355
+ },
2356
+ "state": {
2357
+ "type": "string",
2358
+ "enum": [
2359
+ "none",
2360
+ "closed",
2361
+ "absent",
2362
+ "open",
2363
+ "present"
2364
+ ]
2365
+ },
2366
+ "amount": {
2367
+ "type": [
2368
+ "string",
2369
+ "number"
2370
+ ]
2371
+ },
2372
+ "notional": {
2373
+ "type": [
2374
+ "string",
2375
+ "number"
2376
+ ]
2377
+ },
2378
+ "size": {
2379
+ "type": [
2380
+ "string",
2381
+ "number"
2382
+ ]
2383
+ },
2384
+ "leverage": {
2385
+ "type": "number"
2386
+ },
2387
+ "order_type": {
2388
+ "type": "string"
2389
+ },
2390
+ "orderType": {
2391
+ "type": "string"
2392
+ },
2393
+ "limit_price": {
2394
+ "type": "number"
2395
+ },
2396
+ "limitPrice": {
2397
+ "type": "number"
2398
+ },
2399
+ "offset_pct": {
2400
+ "type": "number"
2401
+ },
2402
+ "offsetPct": {
2403
+ "type": "number"
2404
+ },
2405
+ "current_price": {
2406
+ "type": "number"
2407
+ },
2408
+ "currentPrice": {
2409
+ "type": "number"
2410
+ },
2411
+ "max_slippage_bps": {
2412
+ "type": "number"
2413
+ },
2414
+ "maxSlippageBps": {
2415
+ "type": "number"
2416
+ },
2417
+ "close_attempts": {
2418
+ "type": "integer"
2419
+ },
2420
+ "close_retry_delay_ms": {
2421
+ "type": "integer"
2422
+ },
2423
+ "timeout_ms": {
2424
+ "type": "number"
2425
+ }
2426
+ },
2427
+ "additionalProperties": false
2428
+ },
2429
+ "orders": {
2430
+ "type": [
2431
+ "object",
2432
+ "boolean"
2433
+ ],
2434
+ "properties": {
2435
+ "mode": {
2436
+ "type": "string",
2437
+ "enum": [
2438
+ "matching",
2439
+ "all"
2440
+ ]
2441
+ },
2442
+ "market": {
2443
+ "type": "string"
2444
+ },
2445
+ "symbol": {
2446
+ "type": "string"
2447
+ },
2448
+ "markets": {
2449
+ "type": "array",
2450
+ "items": {
2451
+ "type": "string"
2452
+ }
2453
+ },
2454
+ "symbols": {
2455
+ "type": "array",
2456
+ "items": {
2457
+ "type": "string"
2458
+ }
2459
+ },
2460
+ "side": {
2461
+ "type": "string",
2462
+ "enum": [
2463
+ "long",
2464
+ "short"
2465
+ ]
2466
+ },
2467
+ "state": {
2468
+ "type": "string",
2469
+ "enum": [
2470
+ "none",
2471
+ "closed",
2472
+ "absent",
2473
+ "open",
2474
+ "present"
2475
+ ]
2476
+ },
2477
+ "amount": {
2478
+ "type": [
2479
+ "string",
2480
+ "number"
2481
+ ]
2482
+ },
2483
+ "notional": {
2484
+ "type": [
2485
+ "string",
2486
+ "number"
2487
+ ]
2488
+ },
2489
+ "size": {
2490
+ "type": [
2491
+ "string",
2492
+ "number"
2493
+ ]
2494
+ },
2495
+ "leverage": {
2496
+ "type": "number"
2497
+ },
2498
+ "order_type": {
2499
+ "type": "string"
2500
+ },
2501
+ "orderType": {
2502
+ "type": "string"
2503
+ },
2504
+ "limit_price": {
2505
+ "type": "number"
2506
+ },
2507
+ "limitPrice": {
2508
+ "type": "number"
2509
+ },
2510
+ "offset_pct": {
2511
+ "type": "number"
2512
+ },
2513
+ "offsetPct": {
2514
+ "type": "number"
2515
+ },
2516
+ "current_price": {
2517
+ "type": "number"
2518
+ },
2519
+ "currentPrice": {
2520
+ "type": "number"
2521
+ },
2522
+ "max_slippage_bps": {
2523
+ "type": "number"
2524
+ },
2525
+ "maxSlippageBps": {
2526
+ "type": "number"
2527
+ },
2528
+ "close_attempts": {
2529
+ "type": "integer"
2530
+ },
2531
+ "close_retry_delay_ms": {
2532
+ "type": "integer"
2533
+ },
2534
+ "timeout_ms": {
2535
+ "type": "number"
2536
+ }
2537
+ },
2538
+ "additionalProperties": false
2539
+ },
2540
+ "hud": {
1740
2541
  "type": "object",
1741
- "description": "Optional structured selector with mode, symbols/markets, and side."
1742
- },
1743
- "side": {
1744
- "type": "string",
1745
- "enum": [
1746
- "long",
1747
- "short"
1748
- ],
1749
- "description": "Optional position/order side filter."
2542
+ "properties": {},
2543
+ "additionalProperties": false
1750
2544
  },
1751
2545
  "timeout_ms": {
1752
2546
  "type": "number"
1753
2547
  },
1754
- "state": {
1755
- "type": "string",
1756
- "enum": [
1757
- "none",
1758
- "absent",
1759
- "closed",
1760
- "open",
1761
- "present"
1762
- ],
1763
- "description": "Desired/expected selected collection state."
1764
- }
1765
- },
1766
- "required": [
1767
- "action"
1768
- ]
1769
- },
1770
- "examples": [
1771
- {
1772
- "description": "Ensure no open orders before proof window",
1773
- "node": {
1774
- "action": "metamask.perps.ensure_orders",
1775
- "state": "none",
1776
- "mode": "all",
1777
- "intent": "Converge Perps orders to the requested state",
1778
- "network": "testnet"
1779
- }
1780
- }
1781
- ],
1782
- "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1783
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1784
- },
1785
- {
1786
- "name": "metamask.perps.start_state",
1787
- "owner": "metamask",
1788
- "description": "mobile Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Recommended configurable Perps start state that first restores the fixture-backed unlocked wallet, then composes reusable domain operations before the proof window.",
1789
- "schema": {
1790
- "type": "object",
1791
- "properties": {
1792
- "action": {
1793
- "const": "metamask.perps.start_state"
1794
- },
1795
- "profile": {
1796
- "type": "string",
1797
- "description": "Optional profile such as clean_market_testnet, open_position_testnet, open_order_testnet, or provider_mainnet_readonly."
1798
- },
1799
- "account": {
1800
- "type": "string"
1801
- },
1802
- "provider": {
1803
- "type": "string",
1804
- "description": "Requested Perps provider, e.g. hyperliquid."
1805
- },
1806
- "network": {
2548
+ "side": {
1807
2549
  "type": "string",
1808
2550
  "enum": [
1809
- "testnet",
1810
- "mainnet"
2551
+ "long",
2552
+ "short"
1811
2553
  ]
1812
2554
  },
1813
- "market": {
1814
- "type": "string"
1815
- },
1816
- "symbol": {
2555
+ "account_name": {
1817
2556
  "type": "string"
1818
2557
  },
1819
- "markets": {
1820
- "type": "array",
1821
- "items": {
1822
- "type": "string"
1823
- }
1824
- },
1825
- "symbols": {
1826
- "type": "array",
1827
- "items": {
1828
- "type": "string"
1829
- }
1830
- },
1831
- "page": {
1832
- "oneOf": [
1833
- {
1834
- "type": "string"
1835
- },
1836
- {
1837
- "const": false
1838
- }
1839
- ],
1840
- "description": "Final page target; false skips navigation for teardown."
2558
+ "readyToTrade": {
2559
+ "type": "boolean"
1841
2560
  },
1842
- "positions": {
1843
- "oneOf": [
1844
- {
1845
- "type": "object"
2561
+ "balance": {
2562
+ "type": "object",
2563
+ "properties": {
2564
+ "minWithdrawableUsd": {
2565
+ "type": "number"
1846
2566
  },
1847
- {
1848
- "const": false
1849
- }
1850
- ],
1851
- "description": "Desired position baseline or false to skip."
1852
- },
1853
- "orders": {
1854
- "oneOf": [
1855
- {
1856
- "type": "object"
2567
+ "minUsd": {
2568
+ "type": "number"
1857
2569
  },
1858
- {
1859
- "const": false
2570
+ "minSpendableUsd": {
2571
+ "type": "number"
1860
2572
  }
1861
- ],
1862
- "description": "Desired open-order baseline or false to skip."
1863
- },
1864
- "hud": {
1865
- "type": "object"
2573
+ },
2574
+ "additionalProperties": false
1866
2575
  },
1867
- "timeout_ms": {
1868
- "type": "number"
2576
+ "mode": {
2577
+ "type": "string",
2578
+ "enum": [
2579
+ "matching",
2580
+ "all"
2581
+ ]
1869
2582
  }
1870
2583
  },
1871
- "required": [
1872
- "action"
1873
- ]
2584
+ "additionalProperties": false
1874
2585
  },
1875
2586
  "examples": [
1876
2587
  {
@@ -1907,7 +2618,11 @@
1907
2618
  }
1908
2619
  ],
1909
2620
  "proof_effect": "Runs in setup/teardown phase and must be visible in trace.json; proof videos can keep this as trace-only setup.",
1910
- "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof."
2621
+ "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof.",
2622
+ "execution_capabilities": [
2623
+ "app-mutation",
2624
+ "external-mutation"
2625
+ ]
1911
2626
  },
1912
2627
  {
1913
2628
  "name": "metamask.perps.teardown_state",
@@ -1916,9 +2631,6 @@
1916
2631
  "schema": {
1917
2632
  "type": "object",
1918
2633
  "properties": {
1919
- "action": {
1920
- "const": "metamask.perps.teardown_state"
1921
- },
1922
2634
  "profile": {
1923
2635
  "type": "string",
1924
2636
  "description": "Optional profile such as clean_market_testnet, open_position_testnet, open_order_testnet, or provider_mainnet_readonly."
@@ -1956,48 +2668,287 @@
1956
2668
  }
1957
2669
  },
1958
2670
  "page": {
1959
- "oneOf": [
1960
- {
1961
- "type": "string"
1962
- },
1963
- {
1964
- "const": false
1965
- }
2671
+ "description": "Final page target; false skips navigation for teardown.",
2672
+ "type": [
2673
+ "string",
2674
+ "boolean"
1966
2675
  ],
1967
- "description": "Final page target; false skips navigation for teardown."
2676
+ "enum": [
2677
+ "home",
2678
+ "perps",
2679
+ "perps_home",
2680
+ "market",
2681
+ "market_details",
2682
+ false
2683
+ ]
1968
2684
  },
1969
2685
  "positions": {
1970
- "oneOf": [
1971
- {
1972
- "type": "object"
2686
+ "type": [
2687
+ "object",
2688
+ "boolean"
2689
+ ],
2690
+ "properties": {
2691
+ "mode": {
2692
+ "type": "string",
2693
+ "enum": [
2694
+ "matching",
2695
+ "all"
2696
+ ]
1973
2697
  },
1974
- {
1975
- "const": false
2698
+ "market": {
2699
+ "type": "string"
2700
+ },
2701
+ "symbol": {
2702
+ "type": "string"
2703
+ },
2704
+ "markets": {
2705
+ "type": "array",
2706
+ "items": {
2707
+ "type": "string"
2708
+ }
2709
+ },
2710
+ "symbols": {
2711
+ "type": "array",
2712
+ "items": {
2713
+ "type": "string"
2714
+ }
2715
+ },
2716
+ "side": {
2717
+ "type": "string",
2718
+ "enum": [
2719
+ "long",
2720
+ "short"
2721
+ ]
2722
+ },
2723
+ "state": {
2724
+ "type": "string",
2725
+ "enum": [
2726
+ "none",
2727
+ "closed",
2728
+ "absent",
2729
+ "open",
2730
+ "present"
2731
+ ]
2732
+ },
2733
+ "amount": {
2734
+ "type": [
2735
+ "string",
2736
+ "number"
2737
+ ]
2738
+ },
2739
+ "notional": {
2740
+ "type": [
2741
+ "string",
2742
+ "number"
2743
+ ]
2744
+ },
2745
+ "size": {
2746
+ "type": [
2747
+ "string",
2748
+ "number"
2749
+ ]
2750
+ },
2751
+ "leverage": {
2752
+ "type": "number"
2753
+ },
2754
+ "order_type": {
2755
+ "type": "string"
2756
+ },
2757
+ "orderType": {
2758
+ "type": "string"
2759
+ },
2760
+ "limit_price": {
2761
+ "type": "number"
2762
+ },
2763
+ "limitPrice": {
2764
+ "type": "number"
2765
+ },
2766
+ "offset_pct": {
2767
+ "type": "number"
2768
+ },
2769
+ "offsetPct": {
2770
+ "type": "number"
2771
+ },
2772
+ "current_price": {
2773
+ "type": "number"
2774
+ },
2775
+ "currentPrice": {
2776
+ "type": "number"
2777
+ },
2778
+ "max_slippage_bps": {
2779
+ "type": "number"
2780
+ },
2781
+ "maxSlippageBps": {
2782
+ "type": "number"
2783
+ },
2784
+ "close_attempts": {
2785
+ "type": "integer"
2786
+ },
2787
+ "close_retry_delay_ms": {
2788
+ "type": "integer"
2789
+ },
2790
+ "timeout_ms": {
2791
+ "type": "number"
1976
2792
  }
1977
- ],
1978
- "description": "Desired position baseline or false to skip."
2793
+ },
2794
+ "additionalProperties": false
1979
2795
  },
1980
2796
  "orders": {
1981
- "oneOf": [
1982
- {
1983
- "type": "object"
2797
+ "type": [
2798
+ "object",
2799
+ "boolean"
2800
+ ],
2801
+ "properties": {
2802
+ "mode": {
2803
+ "type": "string",
2804
+ "enum": [
2805
+ "matching",
2806
+ "all"
2807
+ ]
1984
2808
  },
1985
- {
1986
- "const": false
2809
+ "market": {
2810
+ "type": "string"
2811
+ },
2812
+ "symbol": {
2813
+ "type": "string"
2814
+ },
2815
+ "markets": {
2816
+ "type": "array",
2817
+ "items": {
2818
+ "type": "string"
2819
+ }
2820
+ },
2821
+ "symbols": {
2822
+ "type": "array",
2823
+ "items": {
2824
+ "type": "string"
2825
+ }
2826
+ },
2827
+ "side": {
2828
+ "type": "string",
2829
+ "enum": [
2830
+ "long",
2831
+ "short"
2832
+ ]
2833
+ },
2834
+ "state": {
2835
+ "type": "string",
2836
+ "enum": [
2837
+ "none",
2838
+ "closed",
2839
+ "absent",
2840
+ "open",
2841
+ "present"
2842
+ ]
2843
+ },
2844
+ "amount": {
2845
+ "type": [
2846
+ "string",
2847
+ "number"
2848
+ ]
2849
+ },
2850
+ "notional": {
2851
+ "type": [
2852
+ "string",
2853
+ "number"
2854
+ ]
2855
+ },
2856
+ "size": {
2857
+ "type": [
2858
+ "string",
2859
+ "number"
2860
+ ]
2861
+ },
2862
+ "leverage": {
2863
+ "type": "number"
2864
+ },
2865
+ "order_type": {
2866
+ "type": "string"
2867
+ },
2868
+ "orderType": {
2869
+ "type": "string"
2870
+ },
2871
+ "limit_price": {
2872
+ "type": "number"
2873
+ },
2874
+ "limitPrice": {
2875
+ "type": "number"
2876
+ },
2877
+ "offset_pct": {
2878
+ "type": "number"
2879
+ },
2880
+ "offsetPct": {
2881
+ "type": "number"
2882
+ },
2883
+ "current_price": {
2884
+ "type": "number"
2885
+ },
2886
+ "currentPrice": {
2887
+ "type": "number"
2888
+ },
2889
+ "max_slippage_bps": {
2890
+ "type": "number"
2891
+ },
2892
+ "maxSlippageBps": {
2893
+ "type": "number"
2894
+ },
2895
+ "close_attempts": {
2896
+ "type": "integer"
2897
+ },
2898
+ "close_retry_delay_ms": {
2899
+ "type": "integer"
2900
+ },
2901
+ "timeout_ms": {
2902
+ "type": "number"
1987
2903
  }
1988
- ],
1989
- "description": "Desired open-order baseline or false to skip."
2904
+ },
2905
+ "additionalProperties": false
1990
2906
  },
1991
2907
  "hud": {
1992
- "type": "object"
2908
+ "type": "object",
2909
+ "properties": {},
2910
+ "additionalProperties": false
1993
2911
  },
1994
2912
  "timeout_ms": {
1995
2913
  "type": "number"
2914
+ },
2915
+ "side": {
2916
+ "type": "string",
2917
+ "enum": [
2918
+ "long",
2919
+ "short"
2920
+ ]
2921
+ },
2922
+ "account_name": {
2923
+ "type": "string"
2924
+ },
2925
+ "readyToTrade": {
2926
+ "type": "boolean"
2927
+ },
2928
+ "balance": {
2929
+ "type": "object",
2930
+ "properties": {
2931
+ "minWithdrawableUsd": {
2932
+ "type": "number"
2933
+ },
2934
+ "minUsd": {
2935
+ "type": "number"
2936
+ },
2937
+ "minSpendableUsd": {
2938
+ "type": "number"
2939
+ }
2940
+ },
2941
+ "additionalProperties": false
2942
+ },
2943
+ "mode": {
2944
+ "type": "string",
2945
+ "enum": [
2946
+ "matching",
2947
+ "all"
2948
+ ]
1996
2949
  }
1997
2950
  },
1998
- "required": [
1999
- "action"
2000
- ]
2951
+ "additionalProperties": false
2001
2952
  },
2002
2953
  "examples": [
2003
2954
  {
@@ -2025,23 +2976,11 @@
2025
2976
  }
2026
2977
  ],
2027
2978
  "proof_effect": "Runs in setup/teardown phase and must be visible in trace.json; proof videos can keep this as trace-only setup.",
2028
- "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof."
2029
- }
2030
- ],
2031
- "state_refs": [
2032
- {
2033
- "name": "wallet.redacted_state",
2034
- "description": "Redacted wallet state reported by metamask.wallet.read_state."
2035
- },
2036
- {
2037
- "name": "perps.positions",
2038
- "description": "Normalized Perps position state."
2039
- }
2040
- ],
2041
- "pre_conditions": [
2042
- {
2043
- "id": "no-mid-recipe-state-injection",
2044
- "description": "Recipes must drive real supported UI/app/API paths for proof state."
2979
+ "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof.",
2980
+ "execution_capabilities": [
2981
+ "app-mutation",
2982
+ "external-mutation"
2983
+ ]
2045
2984
  }
2046
2985
  ],
2047
2986
  "native_bindings": [
@@ -2193,9 +3132,5 @@
2193
3132
  "action": "metamask.perps.teardown_state",
2194
3133
  "implementation": "@metamask/recipe-runner/mobile/perps-domain-dispatcher"
2195
3134
  }
2196
- ],
2197
- "capability_notes": [
2198
- "Perps safety: default to testnet for setup and order/position mutations. Mainnet is read-only unless the user explicitly requests a mainnet mutation."
2199
- ],
2200
- "action_profiles": []
3135
+ ]
2201
3136
  }