@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",
@@ -34,7 +35,28 @@
34
35
  "intent": "Run a shell command from the project root"
35
36
  }
36
37
  }
37
- ]
38
+ ],
39
+ "schema": {
40
+ "type": "object",
41
+ "properties": {
42
+ "cmd": {
43
+ "type": "string"
44
+ },
45
+ "command": {
46
+ "type": "string"
47
+ },
48
+ "cwd": {
49
+ "type": "string"
50
+ },
51
+ "timeout_ms": {
52
+ "type": "number"
53
+ },
54
+ "allow_failure": {
55
+ "type": "boolean"
56
+ }
57
+ },
58
+ "additionalProperties": false
59
+ }
38
60
  },
39
61
  "wait": {
40
62
  "description": "Base wait action.",
@@ -46,7 +68,44 @@
46
68
  "intent": "Base wait action"
47
69
  }
48
70
  }
49
- ]
71
+ ],
72
+ "schema": {
73
+ "type": "object",
74
+ "properties": {
75
+ "duration_ms": {
76
+ "type": "integer"
77
+ },
78
+ "ms": {
79
+ "type": "integer"
80
+ }
81
+ },
82
+ "additionalProperties": false
83
+ }
84
+ },
85
+ "switch": {
86
+ "description": "Choose a graph branch when one resolved string equals another.",
87
+ "examples": [
88
+ {
89
+ "node": {
90
+ "action": "switch",
91
+ "value": "{{params.mode}}",
92
+ "equals": "background_resume",
93
+ "cases": { "match": "background" },
94
+ "default": "warm",
95
+ "intent": "Choose the requested lifecycle preparation path"
96
+ }
97
+ }
98
+ ],
99
+ "schema": {
100
+ "type": "object",
101
+ "properties": {
102
+ "value": { "type": "string" },
103
+ "equals": { "type": "string" }
104
+ },
105
+ "required": ["value", "equals"],
106
+ "additionalProperties": false
107
+ },
108
+ "result_cases": ["match"]
50
109
  },
51
110
  "watch_logs": {
52
111
  "description": "Base log assertion/capture action.",
@@ -58,7 +117,29 @@
58
117
  "intent": "Base log assertion/capture action"
59
118
  }
60
119
  }
61
- ]
120
+ ],
121
+ "schema": {
122
+ "type": "object",
123
+ "properties": {
124
+ "path": {
125
+ "type": "string"
126
+ },
127
+ "contains": {
128
+ "type": "string"
129
+ },
130
+ "scope": {
131
+ "type": "string",
132
+ "enum": [
133
+ "run",
134
+ "file"
135
+ ]
136
+ }
137
+ },
138
+ "required": [
139
+ "path"
140
+ ],
141
+ "additionalProperties": false
142
+ }
62
143
  },
63
144
  "index_artifacts": {
64
145
  "description": "Index extra recipe-authored artifacts that already exist on disk. Do not use after ui.screenshot; screenshots are registered by the runner.",
@@ -77,7 +158,55 @@
77
158
  "intent": "Register a pre-existing report artifact"
78
159
  }
79
160
  }
80
- ]
161
+ ],
162
+ "schema": {
163
+ "type": "object",
164
+ "properties": {
165
+ "artifacts": {
166
+ "type": "array",
167
+ "items": {
168
+ "type": [
169
+ "string",
170
+ "object"
171
+ ],
172
+ "properties": {
173
+ "path": {
174
+ "type": "string"
175
+ },
176
+ "type": {
177
+ "type": "string"
178
+ },
179
+ "label": {
180
+ "type": "string"
181
+ },
182
+ "nodeId": {
183
+ "type": "string"
184
+ },
185
+ "mimeType": {
186
+ "type": "string"
187
+ },
188
+ "category": {
189
+ "type": "string"
190
+ },
191
+ "proofTarget": {
192
+ "type": "string"
193
+ },
194
+ "covers": {
195
+ "type": "array",
196
+ "items": {
197
+ "type": "string"
198
+ }
199
+ }
200
+ },
201
+ "additionalProperties": false
202
+ }
203
+ }
204
+ },
205
+ "required": [
206
+ "artifacts"
207
+ ],
208
+ "additionalProperties": false
209
+ }
81
210
  },
82
211
  "end": {
83
212
  "description": "Base terminal action.",
@@ -101,30 +230,37 @@
101
230
  "home",
102
231
  "perps",
103
232
  "perps-market"
104
- ],
105
- "description": "Small stable page aliases; use raw route/hash when you need a route outside this list."
233
+ ]
106
234
  },
107
235
  "market": {
108
- "type": "string",
109
- "description": "Market symbol for page=perps-market, e.g. ETH."
236
+ "type": "string"
110
237
  },
111
238
  "symbol": {
112
- "type": "string",
113
- "description": "Alias for market."
239
+ "type": "string"
114
240
  },
115
241
  "url": {
116
- "type": "string",
117
- "description": "Raw extension URL."
242
+ "type": "string"
118
243
  },
119
244
  "hash": {
120
- "type": "string",
121
- "description": "Raw extension hash, e.g. #/perps-home."
245
+ "type": "string"
122
246
  },
123
247
  "path": {
124
- "type": "string",
125
- "description": "Alias for hash."
248
+ "type": "string"
249
+ },
250
+ "route": {
251
+ "type": "string"
252
+ },
253
+ "screen": {
254
+ "type": "string"
255
+ },
256
+ "params": {
257
+ "type": "object"
258
+ },
259
+ "timeout_ms": {
260
+ "type": "number"
126
261
  }
127
- }
262
+ },
263
+ "additionalProperties": false
128
264
  },
129
265
  "examples": [
130
266
  {
@@ -160,11 +296,38 @@
160
296
  {
161
297
  "node": {
162
298
  "action": "ui.press",
163
- "target": "Perps",
164
- "intent": "Press/tap/click a UI target through the platform adapter"
299
+ "intent": "Press/tap/click a UI target through the platform adapter",
300
+ "text": "Perps"
165
301
  }
166
302
  }
167
- ]
303
+ ],
304
+ "schema": {
305
+ "type": "object",
306
+ "properties": {
307
+ "selector": {
308
+ "type": "string"
309
+ },
310
+ "test_id": {
311
+ "type": "string"
312
+ },
313
+ "testID": {
314
+ "type": "string"
315
+ },
316
+ "text": {
317
+ "type": "string"
318
+ },
319
+ "label": {
320
+ "type": "string"
321
+ },
322
+ "timeout_ms": {
323
+ "type": "number"
324
+ },
325
+ "settle": {
326
+ "type": "boolean"
327
+ }
328
+ },
329
+ "additionalProperties": false
330
+ }
168
331
  },
169
332
  "ui.scroll": {
170
333
  "description": "Scroll through the UI through the Farmslot base platform adapter. Supports deltas and scroll_into_view for selector/test-id proof positioning.",
@@ -186,27 +349,87 @@
186
349
  }
187
350
  }
188
351
  ],
189
- "proof_effect": "E2E validation must include a passing ui-scroll-into-view trace event with output.intoView=true."
352
+ "proof_effect": "E2E validation must include a passing ui-scroll-into-view trace event with output.intoView=true.",
353
+ "schema": {
354
+ "type": "object",
355
+ "properties": {
356
+ "selector": {
357
+ "type": "string"
358
+ },
359
+ "test_id": {
360
+ "type": "string"
361
+ },
362
+ "testID": {
363
+ "type": "string"
364
+ },
365
+ "delta_x": {
366
+ "type": "number"
367
+ },
368
+ "delta_y": {
369
+ "type": "number"
370
+ },
371
+ "deltaX": {
372
+ "type": "number"
373
+ },
374
+ "deltaY": {
375
+ "type": "number"
376
+ },
377
+ "offset": {
378
+ "type": "number"
379
+ },
380
+ "scroll_into_view": {
381
+ "type": "boolean"
382
+ },
383
+ "into_view": {
384
+ "type": "boolean"
385
+ },
386
+ "animated": {
387
+ "type": "boolean"
388
+ },
389
+ "timeout_ms": {
390
+ "type": "number"
391
+ },
392
+ "settle": {
393
+ "type": "boolean"
394
+ }
395
+ },
396
+ "additionalProperties": false
397
+ }
190
398
  },
191
399
  "ui.wait_for": {
192
400
  "description": "Wait for a UI condition through the Extension CDP adapter.",
193
401
  "schema": {
194
402
  "type": "object",
195
403
  "properties": {
196
- "action": { "const": "ui.wait_for" },
197
- "selector": { "type": "string" },
198
- "text": { "type": "string" },
404
+ "selector": {
405
+ "type": "string"
406
+ },
407
+ "test_id": {
408
+ "type": "string"
409
+ },
410
+ "testID": {
411
+ "type": "string"
412
+ },
413
+ "text": {
414
+ "type": "string"
415
+ },
416
+ "text_match": {
417
+ "type": "string"
418
+ },
419
+ "textMatch": {
420
+ "type": "string"
421
+ },
199
422
  "expected": {
200
- "type": "string",
201
- "enum": ["present", "absent", "visible", "hidden", "not_present"]
423
+ "type": "string"
202
424
  },
203
425
  "visible": {
204
- "type": "boolean",
205
- "description": "Convenience alias: true means expected=visible; false means expected=absent."
426
+ "type": "boolean"
206
427
  },
207
- "timeout_ms": { "type": "number" }
428
+ "timeout_ms": {
429
+ "type": "number"
430
+ }
208
431
  },
209
- "required": ["action"]
432
+ "additionalProperties": false
210
433
  },
211
434
  "examples": [
212
435
  {
@@ -237,7 +460,25 @@
237
460
  "intent": "Capture visual evidence after a wait/assert step"
238
461
  }
239
462
  }
240
- ]
463
+ ],
464
+ "schema": {
465
+ "type": "object",
466
+ "properties": {
467
+ "path": {
468
+ "type": "string"
469
+ },
470
+ "label": {
471
+ "type": "string"
472
+ },
473
+ "category": {
474
+ "type": "string"
475
+ },
476
+ "timeout_ms": {
477
+ "type": "number"
478
+ }
479
+ },
480
+ "additionalProperties": false
481
+ }
241
482
  },
242
483
  "assert_file": {
243
484
  "description": "Assert that a project file exists and optionally contains text.",
@@ -250,7 +491,22 @@
250
491
  "intent": "Assert that a project file exists and optionally contains text"
251
492
  }
252
493
  }
253
- ]
494
+ ],
495
+ "schema": {
496
+ "type": "object",
497
+ "properties": {
498
+ "path": {
499
+ "type": "string"
500
+ },
501
+ "contains": {
502
+ "type": "string"
503
+ }
504
+ },
505
+ "required": [
506
+ "path"
507
+ ],
508
+ "additionalProperties": false
509
+ }
254
510
  },
255
511
  "assert_json": {
256
512
  "description": "Assert a JSON file value with a JSONPath-style selector.",
@@ -267,7 +523,69 @@
267
523
  "intent": "Assert a JSON file value with a JSONPath-style selector"
268
524
  }
269
525
  }
270
- ]
526
+ ],
527
+ "schema": {
528
+ "type": "object",
529
+ "properties": {
530
+ "path": {
531
+ "type": "string"
532
+ },
533
+ "assert": {
534
+ "type": "object",
535
+ "properties": {
536
+ "path": {
537
+ "type": "string"
538
+ },
539
+ "operator": {
540
+ "type": "string"
541
+ },
542
+ "value": {
543
+ "type": [
544
+ "string",
545
+ "number",
546
+ "integer",
547
+ "boolean",
548
+ "object",
549
+ "array"
550
+ ],
551
+ "items": {
552
+ "type": [
553
+ "string",
554
+ "number",
555
+ "integer",
556
+ "boolean",
557
+ "object"
558
+ ]
559
+ }
560
+ },
561
+ "all": {
562
+ "type": "array",
563
+ "items": {
564
+ "type": "object"
565
+ }
566
+ },
567
+ "any": {
568
+ "type": "array",
569
+ "items": {
570
+ "type": "object"
571
+ }
572
+ },
573
+ "none": {
574
+ "type": "array",
575
+ "items": {
576
+ "type": "object"
577
+ }
578
+ }
579
+ },
580
+ "additionalProperties": false
581
+ }
582
+ },
583
+ "required": [
584
+ "path",
585
+ "assert"
586
+ ],
587
+ "additionalProperties": false
588
+ }
271
589
  },
272
590
  "assert_exit_code": {
273
591
  "description": "Assert the exit code captured from a command node output.",
@@ -280,7 +598,22 @@
280
598
  "intent": "Assert the exit code captured from a command node output"
281
599
  }
282
600
  }
283
- ]
601
+ ],
602
+ "schema": {
603
+ "type": "object",
604
+ "properties": {
605
+ "source": {
606
+ "type": "string"
607
+ },
608
+ "node": {
609
+ "type": "string"
610
+ },
611
+ "expected": {
612
+ "type": "number"
613
+ }
614
+ },
615
+ "additionalProperties": false
616
+ }
284
617
  },
285
618
  "assert_output": {
286
619
  "description": "Assert stdout or stderr captured from a command node output.",
@@ -294,17 +627,87 @@
294
627
  "intent": "Assert stdout or stderr captured from a command node output"
295
628
  }
296
629
  }
297
- ]
298
- },
299
- "app.hud": {
300
- "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.",
301
- "examples": [
302
- {
303
- "description": "Default one-intent progress HUD",
304
- "node": {
305
- "action": "app.hud",
306
- "status": "running",
307
- "intent": "Open a small ETH long position",
630
+ ],
631
+ "schema": {
632
+ "type": "object",
633
+ "properties": {
634
+ "source": {
635
+ "type": "string"
636
+ },
637
+ "node": {
638
+ "type": "string"
639
+ },
640
+ "stream": {
641
+ "type": "string"
642
+ },
643
+ "contains": {
644
+ "type": "string"
645
+ },
646
+ "match": {
647
+ "type": "string"
648
+ },
649
+ "assert": {
650
+ "type": "object",
651
+ "properties": {
652
+ "path": {
653
+ "type": "string"
654
+ },
655
+ "operator": {
656
+ "type": "string"
657
+ },
658
+ "value": {
659
+ "type": [
660
+ "string",
661
+ "number",
662
+ "integer",
663
+ "boolean",
664
+ "object",
665
+ "array"
666
+ ],
667
+ "items": {
668
+ "type": [
669
+ "string",
670
+ "number",
671
+ "integer",
672
+ "boolean",
673
+ "object"
674
+ ]
675
+ }
676
+ },
677
+ "all": {
678
+ "type": "array",
679
+ "items": {
680
+ "type": "object"
681
+ }
682
+ },
683
+ "any": {
684
+ "type": "array",
685
+ "items": {
686
+ "type": "object"
687
+ }
688
+ },
689
+ "none": {
690
+ "type": "array",
691
+ "items": {
692
+ "type": "object"
693
+ }
694
+ }
695
+ },
696
+ "additionalProperties": false
697
+ }
698
+ },
699
+ "additionalProperties": false
700
+ }
701
+ },
702
+ "app.hud": {
703
+ "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.",
704
+ "examples": [
705
+ {
706
+ "description": "Default one-intent progress HUD",
707
+ "node": {
708
+ "action": "app.hud",
709
+ "status": "running",
710
+ "intent": "Open a small ETH long position",
308
711
  "progress": {
309
712
  "current": 12,
310
713
  "total": 19
@@ -316,6 +719,12 @@
316
719
  "schema": {
317
720
  "type": "object",
318
721
  "properties": {
722
+ "clear": {
723
+ "type": "boolean"
724
+ },
725
+ "title": {
726
+ "type": "string"
727
+ },
319
728
  "status": {
320
729
  "type": "string",
321
730
  "enum": [
@@ -324,23 +733,23 @@
324
733
  "fail"
325
734
  ]
326
735
  },
327
- "intent": {
736
+ "node_id": {
328
737
  "type": "string"
329
738
  },
330
- "progress": {
331
- "type": "object",
332
- "properties": {
333
- "current": {
334
- "type": "number"
335
- },
336
- "total": {
337
- "type": "number"
338
- }
339
- },
340
- "required": [
341
- "current",
342
- "total"
343
- ]
739
+ "nodeId": {
740
+ "type": "string"
741
+ },
742
+ "phase": {
743
+ "type": "string"
744
+ },
745
+ "action_name": {
746
+ "type": "string"
747
+ },
748
+ "recipe_action": {
749
+ "type": "string"
750
+ },
751
+ "text": {
752
+ "type": "string"
344
753
  },
345
754
  "detail": {
346
755
  "type": "string"
@@ -349,29 +758,13 @@
349
758
  "type": "string"
350
759
  },
351
760
  "display": {
352
- "type": "object",
353
- "properties": {
354
- "showDetail": {
355
- "type": "boolean"
356
- },
357
- "showDebug": {
358
- "type": "boolean"
359
- },
360
- "showTitle": {
361
- "type": "boolean"
362
- },
363
- "layout": {
364
- "type": "string"
365
- },
366
- "position": {
367
- "type": "string"
368
- }
369
- }
761
+ "type": "object"
370
762
  },
371
- "clear": {
372
- "type": "boolean"
763
+ "progress": {
764
+ "type": "object"
373
765
  }
374
- }
766
+ },
767
+ "additionalProperties": false
375
768
  }
376
769
  },
377
770
  "ui.set_input": {
@@ -385,17 +778,23 @@
385
778
  "test_id": {
386
779
  "type": "string"
387
780
  },
781
+ "testID": {
782
+ "type": "string"
783
+ },
388
784
  "value": {
389
785
  "type": "string"
390
786
  },
391
787
  "text": {
392
788
  "type": "string"
393
789
  },
394
- "next": {
395
- "type": "string"
790
+ "timeout_ms": {
791
+ "type": "number"
792
+ },
793
+ "settle": {
794
+ "type": "boolean"
396
795
  }
397
796
  },
398
- "additionalProperties": true
797
+ "additionalProperties": false
399
798
  },
400
799
  "examples": [
401
800
  {
@@ -416,363 +815,95 @@
416
815
  "key": {
417
816
  "type": "string"
418
817
  },
419
- "next": {
420
- "type": "string"
421
- }
422
- },
423
- "required": [
424
- "key"
425
- ],
426
- "additionalProperties": true
427
- },
428
- "examples": [
429
- {
430
- "node": {
431
- "action": "ui.key_press",
432
- "key": "Enter",
433
- "intent": "Send a trusted Enter key press to the focused UI element"
434
- }
435
- }
436
- ]
437
- },
438
- "app.status": {
439
- "description": "Report the adapter's static status — platform, project root, resolved checkout shape, and headless compatibility mode (no live route or account).",
440
- "examples": [
441
- {
442
- "node": {
443
- "action": "app.status",
444
- "intent": "Report the resolved checkout status"
445
- }
446
- }
447
- ]
448
- },
449
- "cdp.target": {
450
- "description": "Probe the platform debug transport — Chrome CDP for extension, the React Native debug bridge for mobile — and report whether it is reachable.",
451
- "examples": [
452
- {
453
- "node": {
454
- "action": "cdp.target",
455
- "cdp_port": 9323,
456
- "required": true,
457
- "intent": "Confirm the extension CDP endpoint is reachable"
458
- }
459
- }
460
- ]
461
- },
462
- "metamask.wallet.fixture_status": {
463
- "description": "Report the on-disk wallet fixture status (accounts and password presence) without launching or mutating the app.",
464
- "examples": [
465
- {
466
- "description": "Minimal node",
467
- "node": {
468
- "action": "metamask.wallet.fixture_status",
469
- "intent": "Check wallet fixture status"
470
- }
471
- }
472
- ]
473
- },
474
- "metamask.wallet.setup": {
475
- "description": "Import the wallet fixture accounts and password so the app starts from a known signed-in state.",
476
- "examples": [
477
- {
478
- "description": "Minimal node",
479
- "node": {
480
- "action": "metamask.wallet.setup",
481
- "intent": "Prepare the wallet fixture for recipe execution"
482
- }
483
- }
484
- ]
485
- },
486
- "metamask.wallet.ensure_unlocked": {
487
- "description": "Unlock the wallet with the fixture password when it is currently locked.",
488
- "examples": [
489
- {
490
- "description": "Minimal node",
491
- "node": {
492
- "action": "metamask.wallet.ensure_unlocked",
493
- "intent": "Ensure the wallet is unlocked before proof steps"
494
- }
495
- }
496
- ]
497
- },
498
- "metamask.wallet.select_account": {
499
- "description": "Select a wallet account by address, id, or display name.",
500
- "examples": [
501
- {
502
- "description": "Select by deterministic fixture address",
503
- "node": {
504
- "action": "metamask.wallet.select_account",
505
- "address": "0x8dc623e964475d4d669da601fd15ea9125469003",
506
- "intent": "Select the requested wallet account"
507
- }
508
- },
509
- {
510
- "description": "Select by fixture display name",
511
- "node": {
512
- "action": "metamask.wallet.select_account",
513
- "name": "dev1",
514
- "intent": "Select the requested wallet account"
515
- }
516
- }
517
- ]
518
- },
519
- "metamask.wallet.list_accounts": {
520
- "description": "List redacted wallet accounts and identify the selected account without mutating the app.",
521
- "examples": [
522
- {
523
- "description": "List wallet accounts",
524
- "node": {
525
- "action": "metamask.wallet.list_accounts",
526
- "intent": "Discover the available wallet accounts"
527
- }
528
- }
529
- ]
530
- },
531
- "metamask.wallet.read_state": {
532
- "description": "Read the redacted wallet state (selected account, route, and device) from the running app.",
533
- "examples": [
534
- {
535
- "description": "Minimal node",
536
- "node": {
537
- "action": "metamask.wallet.read_state",
538
- "intent": "Read wallet state needed for the proof"
539
- }
540
- }
541
- ]
542
- },
543
- "metamask.perps.read_positions": {
544
- "description": "Read live Perps positions and return the selected subset.",
545
- "examples": [
546
- {
547
- "description": "Read selected live state",
548
- "node": {
549
- "action": "metamask.perps.read_positions",
550
- "market": "BTC",
551
- "intent": "Read Perps positions needed for the proof"
552
- }
553
- }
554
- ]
555
- },
556
- "metamask.perps.read_orders": {
557
- "description": "Read live Perps open orders and return the selected subset.",
558
- "examples": [
559
- {
560
- "description": "Read selected live state",
561
- "node": {
562
- "action": "metamask.perps.read_orders",
563
- "market": "BTC",
564
- "intent": "Read Perps open orders needed for the proof"
565
- }
566
- }
567
- ]
568
- },
569
- "metamask.perps.close_positions": {
570
- "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.",
571
- "examples": [
572
- {
573
- "description": "Close every open position",
574
- "node": {
575
- "action": "metamask.perps.close_positions",
576
- "mode": "all",
577
- "intent": "Close selected Perps positions before continuing",
578
- "network": "testnet"
579
- }
580
- },
581
- {
582
- "description": "Close selected BTC/ETH positions",
583
- "node": {
584
- "action": "metamask.perps.close_positions",
585
- "markets": [
586
- "BTC",
587
- "ETH"
588
- ],
589
- "intent": "Close selected Perps positions before continuing",
590
- "network": "testnet"
591
- }
592
- }
593
- ]
594
- },
595
- "metamask.perps.close_orders": {
596
- "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.",
597
- "examples": [
598
- {
599
- "description": "Cancel every open order",
600
- "node": {
601
- "action": "metamask.perps.close_orders",
602
- "mode": "all",
603
- "intent": "Cancel selected Perps orders before continuing",
604
- "network": "testnet"
605
- }
606
- },
607
- {
608
- "description": "Cancel BTC orders only",
609
- "node": {
610
- "action": "metamask.perps.close_orders",
611
- "market": "BTC",
612
- "intent": "Cancel selected Perps orders before continuing",
613
- "network": "testnet"
614
- }
615
- }
616
- ]
617
- },
618
- "metamask.perps.place_order": {
619
- "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.",
620
- "examples": [
621
- {
622
- "description": "Place BTC market order",
623
- "node": {
624
- "action": "metamask.perps.place_order",
625
- "market": "BTC",
626
- "side": "long",
627
- "notional": "10",
628
- "leverage": 2,
629
- "intent": "Place the requested Perps order",
630
- "network": "testnet"
631
- }
632
- }
633
- ]
634
- },
635
- "metamask.perps.assert_positions": {
636
- "description": "Assert selected live Perps positions are present or absent.",
637
- "examples": [
638
- {
639
- "description": "Assert BTC position is open",
640
- "node": {
641
- "action": "metamask.perps.assert_positions",
642
- "state": "open",
643
- "market": "BTC",
644
- "intent": "Assert the expected Perps position state"
645
- }
646
- }
647
- ]
648
- },
649
- "metamask.perps.assert_orders": {
650
- "description": "Assert selected live Perps open orders are present or absent.",
651
- "examples": [
652
- {
653
- "description": "Assert no BTC open orders",
654
- "node": {
655
- "action": "metamask.perps.assert_orders",
656
- "state": "none",
657
- "market": "BTC",
658
- "intent": "Assert the expected Perps open order state"
659
- }
660
- }
661
- ]
662
- },
663
- "metamask.perps.ensure_positions": {
664
- "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.",
665
- "examples": [
666
- {
667
- "description": "Ensure no BTC position before proof window",
668
- "node": {
669
- "action": "metamask.perps.ensure_positions",
670
- "state": "none",
671
- "market": "BTC",
672
- "intent": "Converge Perps positions to the requested state",
673
- "network": "testnet"
674
- }
675
- },
676
- {
677
- "description": "Ensure any existing positions are closed",
678
- "node": {
679
- "action": "metamask.perps.ensure_positions",
680
- "state": "none",
681
- "mode": "all",
682
- "intent": "Converge Perps positions to the requested state",
683
- "network": "testnet"
818
+ "timeout_ms": {
819
+ "type": "number"
820
+ },
821
+ "settle": {
822
+ "type": "boolean"
684
823
  }
685
- }
686
- ]
687
- },
688
- "metamask.perps.ensure_orders": {
689
- "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.",
824
+ },
825
+ "required": [
826
+ "key"
827
+ ],
828
+ "additionalProperties": false
829
+ },
690
830
  "examples": [
691
831
  {
692
- "description": "Ensure no open orders before proof window",
693
832
  "node": {
694
- "action": "metamask.perps.ensure_orders",
695
- "state": "none",
696
- "mode": "all",
697
- "intent": "Converge Perps orders to the requested state",
698
- "network": "testnet"
833
+ "action": "ui.key_press",
834
+ "key": "Enter",
835
+ "intent": "Send a trusted Enter key press to the focused UI element"
699
836
  }
700
837
  }
701
838
  ]
702
839
  },
703
- "metamask.perps.start_state": {
704
- "description": "Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Recommended configurable Perps start state that composes reusable domain operations before the proof window.",
840
+ "app.status": {
841
+ "description": "Report the adapter's static status platform, project root, resolved checkout shape, and headless compatibility mode (no live route or account).",
705
842
  "examples": [
706
843
  {
707
- "description": "Clean BTC testnet market state",
708
- "node": {
709
- "action": "metamask.perps.start_state",
710
- "profile": "clean_market_testnet",
711
- "market": "BTC",
712
- "positions": {
713
- "state": "none"
714
- },
715
- "orders": {
716
- "state": "none"
717
- },
718
- "intent": "Prepare the Perps start state for the proof",
719
- "network": "testnet"
720
- }
721
- },
722
- {
723
- "description": "Start with an open ETH long position",
724
844
  "node": {
725
- "action": "metamask.perps.start_state",
726
- "profile": "open_position_testnet",
727
- "market": "ETH",
728
- "side": "long",
729
- "positions": {
730
- "state": "open",
731
- "notional": "10",
732
- "leverage": 2
733
- },
734
- "intent": "Prepare the Perps start state for the proof",
735
- "network": "testnet"
845
+ "action": "app.status",
846
+ "intent": "Report the resolved checkout status"
736
847
  }
737
848
  }
738
- ]
849
+ ],
850
+ "schema": {
851
+ "type": "object",
852
+ "properties": {},
853
+ "additionalProperties": false
854
+ }
739
855
  },
740
- "metamask.perps.teardown_state": {
741
- "description": "Recommended configurable Perps teardown state that restores the selected environment after proof.",
856
+ "cdp.target": {
857
+ "description": "Probe the platform debug transport Chrome CDP for extension, the React Native debug bridge for mobile — and report whether it is reachable.",
742
858
  "examples": [
743
859
  {
744
- "description": "Return BTC test account to clean state",
745
- "node": {
746
- "action": "metamask.perps.teardown_state",
747
- "market": "BTC",
748
- "positions": {
749
- "state": "none"
750
- },
751
- "orders": {
752
- "state": "none"
753
- },
754
- "page": "home",
755
- "intent": "Restore Perps state after the proof"
756
- }
757
- },
758
- {
759
- "description": "Teardown only state created by selected market",
760
860
  "node": {
761
- "action": "metamask.perps.teardown_state",
762
- "market": "BTC",
763
- "intent": "Restore Perps state after the proof"
861
+ "action": "cdp.target",
862
+ "cdp_port": 9323,
863
+ "required": true,
864
+ "intent": "Confirm the extension CDP endpoint is reachable"
764
865
  }
765
866
  }
766
- ]
867
+ ],
868
+ "schema": {
869
+ "type": "object",
870
+ "properties": {
871
+ "cdp_port": {
872
+ "type": "number"
873
+ },
874
+ "metro_port": {
875
+ "type": "number"
876
+ },
877
+ "watcher_port": {
878
+ "type": "number"
879
+ },
880
+ "probe_timeout_ms": {
881
+ "type": "number"
882
+ },
883
+ "cdp_timeout_ms": {
884
+ "type": "number"
885
+ },
886
+ "timeout_ms": {
887
+ "type": "number"
888
+ },
889
+ "require_reachable": {
890
+ "type": "boolean"
891
+ },
892
+ "required": {
893
+ "type": "boolean"
894
+ }
895
+ },
896
+ "additionalProperties": false
897
+ }
767
898
  },
768
899
  "call": {
769
- "description": "Invoke a named sub-recipe flow by ref and run its steps inline.",
900
+ "description": "Invoke a named recipe by ref and run its steps inline.",
770
901
  "examples": [
771
902
  {
772
903
  "node": {
773
904
  "action": "call",
774
905
  "ref": "mydev.open_perps_setup",
775
- "intent": "Run a personal Perps setup flow segment"
906
+ "intent": "Run a personal Perps setup recipe"
776
907
  }
777
908
  }
778
909
  ]
@@ -785,14 +916,8 @@
785
916
  "description": "Report the on-disk wallet fixture status (accounts and password presence) without launching or mutating the app.",
786
917
  "schema": {
787
918
  "type": "object",
788
- "properties": {
789
- "action": {
790
- "const": "metamask.wallet.fixture_status"
791
- }
792
- },
793
- "required": [
794
- "action"
795
- ]
919
+ "properties": {},
920
+ "additionalProperties": false
796
921
  },
797
922
  "examples": [
798
923
  {
@@ -804,7 +929,8 @@
804
929
  }
805
930
  ],
806
931
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
807
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
932
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
933
+ "execution_capabilities": []
808
934
  },
809
935
  {
810
936
  "name": "metamask.wallet.setup",
@@ -812,14 +938,8 @@
812
938
  "description": "Import the wallet fixture accounts and password so the app starts from a known signed-in state.",
813
939
  "schema": {
814
940
  "type": "object",
815
- "properties": {
816
- "action": {
817
- "const": "metamask.wallet.setup"
818
- }
819
- },
820
- "required": [
821
- "action"
822
- ]
941
+ "properties": {},
942
+ "additionalProperties": false
823
943
  },
824
944
  "examples": [
825
945
  {
@@ -831,7 +951,10 @@
831
951
  }
832
952
  ],
833
953
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
834
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
954
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
955
+ "execution_capabilities": [
956
+ "app-mutation"
957
+ ]
835
958
  },
836
959
  {
837
960
  "name": "metamask.wallet.ensure_unlocked",
@@ -840,13 +963,11 @@
840
963
  "schema": {
841
964
  "type": "object",
842
965
  "properties": {
843
- "action": {
844
- "const": "metamask.wallet.ensure_unlocked"
966
+ "timeout_ms": {
967
+ "type": "number"
845
968
  }
846
969
  },
847
- "required": [
848
- "action"
849
- ]
970
+ "additionalProperties": false
850
971
  },
851
972
  "examples": [
852
973
  {
@@ -858,7 +979,10 @@
858
979
  }
859
980
  ],
860
981
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
861
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
982
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
983
+ "execution_capabilities": [
984
+ "app-mutation"
985
+ ]
862
986
  },
863
987
  {
864
988
  "name": "metamask.wallet.select_account",
@@ -866,9 +990,6 @@
866
990
  "schema": {
867
991
  "type": "object",
868
992
  "properties": {
869
- "action": {
870
- "const": "metamask.wallet.select_account"
871
- },
872
993
  "address": {
873
994
  "type": "string",
874
995
  "description": "EVM address of the account to select."
@@ -882,26 +1003,7 @@
882
1003
  "description": "Display name of the account to select."
883
1004
  }
884
1005
  },
885
- "required": [
886
- "action"
887
- ],
888
- "anyOf": [
889
- {
890
- "required": [
891
- "address"
892
- ]
893
- },
894
- {
895
- "required": [
896
- "id"
897
- ]
898
- },
899
- {
900
- "required": [
901
- "name"
902
- ]
903
- }
904
- ]
1006
+ "additionalProperties": false
905
1007
  },
906
1008
  "examples": [
907
1009
  {
@@ -921,7 +1023,10 @@
921
1023
  }
922
1024
  }
923
1025
  ],
924
- "owner": "metamask"
1026
+ "owner": "metamask",
1027
+ "execution_capabilities": [
1028
+ "app-mutation"
1029
+ ]
925
1030
  },
926
1031
  {
927
1032
  "name": "metamask.wallet.list_accounts",
@@ -930,11 +1035,9 @@
930
1035
  "schema": {
931
1036
  "type": "object",
932
1037
  "properties": {
933
- "action": {
934
- "const": "metamask.wallet.list_accounts"
935
- },
936
1038
  "scope": {
937
1039
  "type": "string",
1040
+ "default": "named",
938
1041
  "enum": [
939
1042
  "selected",
940
1043
  "named",
@@ -944,9 +1047,7 @@
944
1047
  "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."
945
1048
  }
946
1049
  },
947
- "required": [
948
- "action"
949
- ]
1050
+ "additionalProperties": false
950
1051
  },
951
1052
  "examples": [
952
1053
  {
@@ -958,7 +1059,8 @@
958
1059
  }
959
1060
  ],
960
1061
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
961
- "safety_notes": "Read-only account metadata; must never expose keys, mnemonics, vault data, or credentials."
1062
+ "safety_notes": "Read-only account metadata; must never expose keys, mnemonics, vault data, or credentials.",
1063
+ "execution_capabilities": []
962
1064
  },
963
1065
  {
964
1066
  "name": "metamask.wallet.read_state",
@@ -966,14 +1068,8 @@
966
1068
  "description": "Read the redacted wallet state (selected account, route, and device) from the running app.",
967
1069
  "schema": {
968
1070
  "type": "object",
969
- "properties": {
970
- "action": {
971
- "const": "metamask.wallet.read_state"
972
- }
973
- },
974
- "required": [
975
- "action"
976
- ]
1071
+ "properties": {},
1072
+ "additionalProperties": false
977
1073
  },
978
1074
  "examples": [
979
1075
  {
@@ -985,18 +1081,16 @@
985
1081
  }
986
1082
  ],
987
1083
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
988
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1084
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1085
+ "execution_capabilities": []
989
1086
  },
990
1087
  {
991
1088
  "name": "metamask.perps.read_positions",
992
1089
  "owner": "metamask",
993
- "description": "extension Read live Perps positions and return the selected subset.",
1090
+ "description": "extension Read live Perps positions; without a selector, return all live positions.",
994
1091
  "schema": {
995
1092
  "type": "object",
996
1093
  "properties": {
997
- "action": {
998
- "const": "metamask.perps.read_positions"
999
- },
1000
1094
  "market": {
1001
1095
  "type": "string",
1002
1096
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1029,7 +1123,41 @@
1029
1123
  },
1030
1124
  "selector": {
1031
1125
  "type": "object",
1032
- "description": "Optional structured selector with mode, symbols/markets, and side."
1126
+ "properties": {
1127
+ "mode": {
1128
+ "type": "string",
1129
+ "enum": [
1130
+ "matching",
1131
+ "all"
1132
+ ]
1133
+ },
1134
+ "market": {
1135
+ "type": "string"
1136
+ },
1137
+ "symbol": {
1138
+ "type": "string"
1139
+ },
1140
+ "markets": {
1141
+ "type": "array",
1142
+ "items": {
1143
+ "type": "string"
1144
+ }
1145
+ },
1146
+ "symbols": {
1147
+ "type": "array",
1148
+ "items": {
1149
+ "type": "string"
1150
+ }
1151
+ },
1152
+ "side": {
1153
+ "type": "string",
1154
+ "enum": [
1155
+ "long",
1156
+ "short"
1157
+ ]
1158
+ }
1159
+ },
1160
+ "additionalProperties": false
1033
1161
  },
1034
1162
  "side": {
1035
1163
  "type": "string",
@@ -1043,9 +1171,7 @@
1043
1171
  "type": "number"
1044
1172
  }
1045
1173
  },
1046
- "required": [
1047
- "action"
1048
- ]
1174
+ "additionalProperties": false
1049
1175
  },
1050
1176
  "examples": [
1051
1177
  {
@@ -1058,18 +1184,16 @@
1058
1184
  }
1059
1185
  ],
1060
1186
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1061
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1187
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1188
+ "execution_capabilities": []
1062
1189
  },
1063
1190
  {
1064
1191
  "name": "metamask.perps.read_orders",
1065
1192
  "owner": "metamask",
1066
- "description": "extension Read live Perps open orders and return the selected subset.",
1193
+ "description": "extension Read live Perps open orders; without a selector, return all live orders.",
1067
1194
  "schema": {
1068
1195
  "type": "object",
1069
1196
  "properties": {
1070
- "action": {
1071
- "const": "metamask.perps.read_orders"
1072
- },
1073
1197
  "market": {
1074
1198
  "type": "string",
1075
1199
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1102,7 +1226,41 @@
1102
1226
  },
1103
1227
  "selector": {
1104
1228
  "type": "object",
1105
- "description": "Optional structured selector with mode, symbols/markets, and side."
1229
+ "properties": {
1230
+ "mode": {
1231
+ "type": "string",
1232
+ "enum": [
1233
+ "matching",
1234
+ "all"
1235
+ ]
1236
+ },
1237
+ "market": {
1238
+ "type": "string"
1239
+ },
1240
+ "symbol": {
1241
+ "type": "string"
1242
+ },
1243
+ "markets": {
1244
+ "type": "array",
1245
+ "items": {
1246
+ "type": "string"
1247
+ }
1248
+ },
1249
+ "symbols": {
1250
+ "type": "array",
1251
+ "items": {
1252
+ "type": "string"
1253
+ }
1254
+ },
1255
+ "side": {
1256
+ "type": "string",
1257
+ "enum": [
1258
+ "long",
1259
+ "short"
1260
+ ]
1261
+ }
1262
+ },
1263
+ "additionalProperties": false
1106
1264
  },
1107
1265
  "side": {
1108
1266
  "type": "string",
@@ -1116,9 +1274,7 @@
1116
1274
  "type": "number"
1117
1275
  }
1118
1276
  },
1119
- "required": [
1120
- "action"
1121
- ]
1277
+ "additionalProperties": false
1122
1278
  },
1123
1279
  "examples": [
1124
1280
  {
@@ -1131,7 +1287,8 @@
1131
1287
  }
1132
1288
  ],
1133
1289
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1134
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1290
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1291
+ "execution_capabilities": []
1135
1292
  },
1136
1293
  {
1137
1294
  "name": "metamask.perps.close_positions",
@@ -1140,9 +1297,6 @@
1140
1297
  "schema": {
1141
1298
  "type": "object",
1142
1299
  "properties": {
1143
- "action": {
1144
- "const": "metamask.perps.close_positions"
1145
- },
1146
1300
  "market": {
1147
1301
  "type": "string",
1148
1302
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1175,7 +1329,41 @@
1175
1329
  },
1176
1330
  "selector": {
1177
1331
  "type": "object",
1178
- "description": "Optional structured selector with mode, symbols/markets, and side."
1332
+ "properties": {
1333
+ "mode": {
1334
+ "type": "string",
1335
+ "enum": [
1336
+ "matching",
1337
+ "all"
1338
+ ]
1339
+ },
1340
+ "market": {
1341
+ "type": "string"
1342
+ },
1343
+ "symbol": {
1344
+ "type": "string"
1345
+ },
1346
+ "markets": {
1347
+ "type": "array",
1348
+ "items": {
1349
+ "type": "string"
1350
+ }
1351
+ },
1352
+ "symbols": {
1353
+ "type": "array",
1354
+ "items": {
1355
+ "type": "string"
1356
+ }
1357
+ },
1358
+ "side": {
1359
+ "type": "string",
1360
+ "enum": [
1361
+ "long",
1362
+ "short"
1363
+ ]
1364
+ }
1365
+ },
1366
+ "additionalProperties": false
1179
1367
  },
1180
1368
  "side": {
1181
1369
  "type": "string",
@@ -1189,9 +1377,7 @@
1189
1377
  "type": "number"
1190
1378
  }
1191
1379
  },
1192
- "required": [
1193
- "action"
1194
- ]
1380
+ "additionalProperties": false
1195
1381
  },
1196
1382
  "examples": [
1197
1383
  {
@@ -1199,8 +1385,7 @@
1199
1385
  "node": {
1200
1386
  "action": "metamask.perps.close_positions",
1201
1387
  "mode": "all",
1202
- "intent": "Close selected Perps positions before continuing",
1203
- "network": "testnet"
1388
+ "intent": "Close selected Perps positions before continuing"
1204
1389
  }
1205
1390
  },
1206
1391
  {
@@ -1211,13 +1396,16 @@
1211
1396
  "BTC",
1212
1397
  "ETH"
1213
1398
  ],
1214
- "intent": "Close selected Perps positions before continuing",
1215
- "network": "testnet"
1399
+ "intent": "Close selected Perps positions before continuing"
1216
1400
  }
1217
1401
  }
1218
1402
  ],
1219
1403
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1220
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1404
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1405
+ "execution_capabilities": [
1406
+ "app-mutation",
1407
+ "external-mutation"
1408
+ ]
1221
1409
  },
1222
1410
  {
1223
1411
  "name": "metamask.perps.close_orders",
@@ -1226,9 +1414,6 @@
1226
1414
  "schema": {
1227
1415
  "type": "object",
1228
1416
  "properties": {
1229
- "action": {
1230
- "const": "metamask.perps.close_orders"
1231
- },
1232
1417
  "market": {
1233
1418
  "type": "string",
1234
1419
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1261,7 +1446,41 @@
1261
1446
  },
1262
1447
  "selector": {
1263
1448
  "type": "object",
1264
- "description": "Optional structured selector with mode, symbols/markets, and side."
1449
+ "properties": {
1450
+ "mode": {
1451
+ "type": "string",
1452
+ "enum": [
1453
+ "matching",
1454
+ "all"
1455
+ ]
1456
+ },
1457
+ "market": {
1458
+ "type": "string"
1459
+ },
1460
+ "symbol": {
1461
+ "type": "string"
1462
+ },
1463
+ "markets": {
1464
+ "type": "array",
1465
+ "items": {
1466
+ "type": "string"
1467
+ }
1468
+ },
1469
+ "symbols": {
1470
+ "type": "array",
1471
+ "items": {
1472
+ "type": "string"
1473
+ }
1474
+ },
1475
+ "side": {
1476
+ "type": "string",
1477
+ "enum": [
1478
+ "long",
1479
+ "short"
1480
+ ]
1481
+ }
1482
+ },
1483
+ "additionalProperties": false
1265
1484
  },
1266
1485
  "side": {
1267
1486
  "type": "string",
@@ -1275,9 +1494,7 @@
1275
1494
  "type": "number"
1276
1495
  }
1277
1496
  },
1278
- "required": [
1279
- "action"
1280
- ]
1497
+ "additionalProperties": false
1281
1498
  },
1282
1499
  "examples": [
1283
1500
  {
@@ -1285,8 +1502,7 @@
1285
1502
  "node": {
1286
1503
  "action": "metamask.perps.close_orders",
1287
1504
  "mode": "all",
1288
- "intent": "Cancel selected Perps orders before continuing",
1289
- "network": "testnet"
1505
+ "intent": "Cancel selected Perps orders before continuing"
1290
1506
  }
1291
1507
  },
1292
1508
  {
@@ -1294,13 +1510,16 @@
1294
1510
  "node": {
1295
1511
  "action": "metamask.perps.close_orders",
1296
1512
  "market": "BTC",
1297
- "intent": "Cancel selected Perps orders before continuing",
1298
- "network": "testnet"
1513
+ "intent": "Cancel selected Perps orders before continuing"
1299
1514
  }
1300
1515
  }
1301
1516
  ],
1302
1517
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1303
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1518
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1519
+ "execution_capabilities": [
1520
+ "app-mutation",
1521
+ "external-mutation"
1522
+ ]
1304
1523
  },
1305
1524
  {
1306
1525
  "name": "metamask.perps.place_order",
@@ -1309,9 +1528,6 @@
1309
1528
  "schema": {
1310
1529
  "type": "object",
1311
1530
  "properties": {
1312
- "action": {
1313
- "const": "metamask.perps.place_order"
1314
- },
1315
1531
  "market": {
1316
1532
  "type": "string",
1317
1533
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1344,7 +1560,41 @@
1344
1560
  },
1345
1561
  "selector": {
1346
1562
  "type": "object",
1347
- "description": "Optional structured selector with mode, symbols/markets, and side."
1563
+ "properties": {
1564
+ "mode": {
1565
+ "type": "string",
1566
+ "enum": [
1567
+ "matching",
1568
+ "all"
1569
+ ]
1570
+ },
1571
+ "market": {
1572
+ "type": "string"
1573
+ },
1574
+ "symbol": {
1575
+ "type": "string"
1576
+ },
1577
+ "markets": {
1578
+ "type": "array",
1579
+ "items": {
1580
+ "type": "string"
1581
+ }
1582
+ },
1583
+ "symbols": {
1584
+ "type": "array",
1585
+ "items": {
1586
+ "type": "string"
1587
+ }
1588
+ },
1589
+ "side": {
1590
+ "type": "string",
1591
+ "enum": [
1592
+ "long",
1593
+ "short"
1594
+ ]
1595
+ }
1596
+ },
1597
+ "additionalProperties": false
1348
1598
  },
1349
1599
  "side": {
1350
1600
  "type": "string",
@@ -1370,12 +1620,11 @@
1370
1620
  "description": "Base asset size when supported."
1371
1621
  },
1372
1622
  "leverage": {
1373
- "type": "number"
1623
+ "type": "number",
1624
+ "default": 3
1374
1625
  }
1375
1626
  },
1376
- "required": [
1377
- "action"
1378
- ]
1627
+ "additionalProperties": false
1379
1628
  },
1380
1629
  "examples": [
1381
1630
  {
@@ -1386,24 +1635,24 @@
1386
1635
  "side": "long",
1387
1636
  "notional": "10",
1388
1637
  "leverage": 2,
1389
- "intent": "Place the requested Perps order",
1390
- "network": "testnet"
1638
+ "intent": "Place the requested Perps order"
1391
1639
  }
1392
1640
  }
1393
1641
  ],
1394
1642
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1395
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1643
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1644
+ "execution_capabilities": [
1645
+ "app-mutation",
1646
+ "external-mutation"
1647
+ ]
1396
1648
  },
1397
1649
  {
1398
1650
  "name": "metamask.perps.assert_positions",
1399
1651
  "owner": "metamask",
1400
- "description": "extension Assert selected live Perps positions are present or absent.",
1652
+ "description": "extension Assert live Perps positions are present or absent for an explicit market selection or mode=all.",
1401
1653
  "schema": {
1402
1654
  "type": "object",
1403
1655
  "properties": {
1404
- "action": {
1405
- "const": "metamask.perps.assert_positions"
1406
- },
1407
1656
  "market": {
1408
1657
  "type": "string",
1409
1658
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1436,7 +1685,41 @@
1436
1685
  },
1437
1686
  "selector": {
1438
1687
  "type": "object",
1439
- "description": "Optional structured selector with mode, symbols/markets, and side."
1688
+ "properties": {
1689
+ "mode": {
1690
+ "type": "string",
1691
+ "enum": [
1692
+ "matching",
1693
+ "all"
1694
+ ]
1695
+ },
1696
+ "market": {
1697
+ "type": "string"
1698
+ },
1699
+ "symbol": {
1700
+ "type": "string"
1701
+ },
1702
+ "markets": {
1703
+ "type": "array",
1704
+ "items": {
1705
+ "type": "string"
1706
+ }
1707
+ },
1708
+ "symbols": {
1709
+ "type": "array",
1710
+ "items": {
1711
+ "type": "string"
1712
+ }
1713
+ },
1714
+ "side": {
1715
+ "type": "string",
1716
+ "enum": [
1717
+ "long",
1718
+ "short"
1719
+ ]
1720
+ }
1721
+ },
1722
+ "additionalProperties": false
1440
1723
  },
1441
1724
  "side": {
1442
1725
  "type": "string",
@@ -1462,8 +1745,9 @@
1462
1745
  }
1463
1746
  },
1464
1747
  "required": [
1465
- "action"
1466
- ]
1748
+ "state"
1749
+ ],
1750
+ "additionalProperties": false
1467
1751
  },
1468
1752
  "examples": [
1469
1753
  {
@@ -1477,18 +1761,16 @@
1477
1761
  }
1478
1762
  ],
1479
1763
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1480
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1764
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1765
+ "execution_capabilities": []
1481
1766
  },
1482
1767
  {
1483
1768
  "name": "metamask.perps.assert_orders",
1484
1769
  "owner": "metamask",
1485
- "description": "extension Assert selected live Perps open orders are present or absent.",
1770
+ "description": "extension Assert live Perps open orders are present or absent for an explicit market selection or mode=all.",
1486
1771
  "schema": {
1487
1772
  "type": "object",
1488
1773
  "properties": {
1489
- "action": {
1490
- "const": "metamask.perps.assert_orders"
1491
- },
1492
1774
  "market": {
1493
1775
  "type": "string",
1494
1776
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1521,7 +1803,41 @@
1521
1803
  },
1522
1804
  "selector": {
1523
1805
  "type": "object",
1524
- "description": "Optional structured selector with mode, symbols/markets, and side."
1806
+ "properties": {
1807
+ "mode": {
1808
+ "type": "string",
1809
+ "enum": [
1810
+ "matching",
1811
+ "all"
1812
+ ]
1813
+ },
1814
+ "market": {
1815
+ "type": "string"
1816
+ },
1817
+ "symbol": {
1818
+ "type": "string"
1819
+ },
1820
+ "markets": {
1821
+ "type": "array",
1822
+ "items": {
1823
+ "type": "string"
1824
+ }
1825
+ },
1826
+ "symbols": {
1827
+ "type": "array",
1828
+ "items": {
1829
+ "type": "string"
1830
+ }
1831
+ },
1832
+ "side": {
1833
+ "type": "string",
1834
+ "enum": [
1835
+ "long",
1836
+ "short"
1837
+ ]
1838
+ }
1839
+ },
1840
+ "additionalProperties": false
1525
1841
  },
1526
1842
  "side": {
1527
1843
  "type": "string",
@@ -1547,8 +1863,9 @@
1547
1863
  }
1548
1864
  },
1549
1865
  "required": [
1550
- "action"
1551
- ]
1866
+ "state"
1867
+ ],
1868
+ "additionalProperties": false
1552
1869
  },
1553
1870
  "examples": [
1554
1871
  {
@@ -1562,7 +1879,8 @@
1562
1879
  }
1563
1880
  ],
1564
1881
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1565
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
1882
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
1883
+ "execution_capabilities": []
1566
1884
  },
1567
1885
  {
1568
1886
  "name": "metamask.perps.ensure_positions",
@@ -1571,9 +1889,6 @@
1571
1889
  "schema": {
1572
1890
  "type": "object",
1573
1891
  "properties": {
1574
- "action": {
1575
- "const": "metamask.perps.ensure_positions"
1576
- },
1577
1892
  "market": {
1578
1893
  "type": "string",
1579
1894
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1606,7 +1921,41 @@
1606
1921
  },
1607
1922
  "selector": {
1608
1923
  "type": "object",
1609
- "description": "Optional structured selector with mode, symbols/markets, and side."
1924
+ "properties": {
1925
+ "mode": {
1926
+ "type": "string",
1927
+ "enum": [
1928
+ "matching",
1929
+ "all"
1930
+ ]
1931
+ },
1932
+ "market": {
1933
+ "type": "string"
1934
+ },
1935
+ "symbol": {
1936
+ "type": "string"
1937
+ },
1938
+ "markets": {
1939
+ "type": "array",
1940
+ "items": {
1941
+ "type": "string"
1942
+ }
1943
+ },
1944
+ "symbols": {
1945
+ "type": "array",
1946
+ "items": {
1947
+ "type": "string"
1948
+ }
1949
+ },
1950
+ "side": {
1951
+ "type": "string",
1952
+ "enum": [
1953
+ "long",
1954
+ "short"
1955
+ ]
1956
+ }
1957
+ },
1958
+ "additionalProperties": false
1610
1959
  },
1611
1960
  "side": {
1612
1961
  "type": "string",
@@ -1632,8 +1981,9 @@
1632
1981
  }
1633
1982
  },
1634
1983
  "required": [
1635
- "action"
1636
- ]
1984
+ "state"
1985
+ ],
1986
+ "additionalProperties": false
1637
1987
  },
1638
1988
  "examples": [
1639
1989
  {
@@ -1642,8 +1992,7 @@
1642
1992
  "action": "metamask.perps.ensure_positions",
1643
1993
  "state": "none",
1644
1994
  "market": "BTC",
1645
- "intent": "Converge Perps positions to the requested state",
1646
- "network": "testnet"
1995
+ "intent": "Converge Perps positions to the requested state"
1647
1996
  }
1648
1997
  },
1649
1998
  {
@@ -1652,13 +2001,16 @@
1652
2001
  "action": "metamask.perps.ensure_positions",
1653
2002
  "state": "none",
1654
2003
  "mode": "all",
1655
- "intent": "Converge Perps positions to the requested state",
1656
- "network": "testnet"
2004
+ "intent": "Converge Perps positions to the requested state"
1657
2005
  }
1658
2006
  }
1659
2007
  ],
1660
2008
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1661
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
2009
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
2010
+ "execution_capabilities": [
2011
+ "app-mutation",
2012
+ "external-mutation"
2013
+ ]
1662
2014
  },
1663
2015
  {
1664
2016
  "name": "metamask.perps.ensure_orders",
@@ -1667,9 +2019,6 @@
1667
2019
  "schema": {
1668
2020
  "type": "object",
1669
2021
  "properties": {
1670
- "action": {
1671
- "const": "metamask.perps.ensure_orders"
1672
- },
1673
2022
  "market": {
1674
2023
  "type": "string",
1675
2024
  "description": "Single market symbol, e.g. BTC or ETH. Alias: symbol."
@@ -1702,7 +2051,41 @@
1702
2051
  },
1703
2052
  "selector": {
1704
2053
  "type": "object",
1705
- "description": "Optional structured selector with mode, symbols/markets, and side."
2054
+ "properties": {
2055
+ "mode": {
2056
+ "type": "string",
2057
+ "enum": [
2058
+ "matching",
2059
+ "all"
2060
+ ]
2061
+ },
2062
+ "market": {
2063
+ "type": "string"
2064
+ },
2065
+ "symbol": {
2066
+ "type": "string"
2067
+ },
2068
+ "markets": {
2069
+ "type": "array",
2070
+ "items": {
2071
+ "type": "string"
2072
+ }
2073
+ },
2074
+ "symbols": {
2075
+ "type": "array",
2076
+ "items": {
2077
+ "type": "string"
2078
+ }
2079
+ },
2080
+ "side": {
2081
+ "type": "string",
2082
+ "enum": [
2083
+ "long",
2084
+ "short"
2085
+ ]
2086
+ }
2087
+ },
2088
+ "additionalProperties": false
1706
2089
  },
1707
2090
  "side": {
1708
2091
  "type": "string",
@@ -1728,8 +2111,9 @@
1728
2111
  }
1729
2112
  },
1730
2113
  "required": [
1731
- "action"
1732
- ]
2114
+ "state"
2115
+ ],
2116
+ "additionalProperties": false
1733
2117
  },
1734
2118
  "examples": [
1735
2119
  {
@@ -1738,13 +2122,16 @@
1738
2122
  "action": "metamask.perps.ensure_orders",
1739
2123
  "state": "none",
1740
2124
  "mode": "all",
1741
- "intent": "Converge Perps orders to the requested state",
1742
- "network": "testnet"
2125
+ "intent": "Converge Perps orders to the requested state"
1743
2126
  }
1744
2127
  }
1745
2128
  ],
1746
2129
  "proof_effect": "E2E validation must record this action in trace.json; live-proof actions include liveAdapter/proofPath or target runtime output.",
1747
- "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof."
2130
+ "safety_notes": "Must not inject mid-recipe UI/app state to fabricate proof.",
2131
+ "execution_capabilities": [
2132
+ "app-mutation",
2133
+ "external-mutation"
2134
+ ]
1748
2135
  },
1749
2136
  {
1750
2137
  "name": "metamask.perps.start_state",
@@ -1753,9 +2140,6 @@
1753
2140
  "schema": {
1754
2141
  "type": "object",
1755
2142
  "properties": {
1756
- "action": {
1757
- "const": "metamask.perps.start_state"
1758
- },
1759
2143
  "profile": {
1760
2144
  "type": "string",
1761
2145
  "description": "Optional profile such as clean_market_testnet, open_position_testnet, open_order_testnet, or provider_mainnet_readonly."
@@ -1794,40 +2178,284 @@
1794
2178
  },
1795
2179
  "page": {
1796
2180
  "type": "string",
2181
+ "enum": [
2182
+ "home",
2183
+ "perps",
2184
+ "perps_home",
2185
+ "market",
2186
+ "market_details",
2187
+ "trade",
2188
+ "order"
2189
+ ],
1797
2190
  "description": "Initial Perps page target; omit it to use the profile or market default. Extension start_state always enters Perps before convergence."
1798
2191
  },
1799
2192
  "positions": {
1800
- "oneOf": [
1801
- {
1802
- "type": "object"
2193
+ "type": [
2194
+ "object",
2195
+ "boolean"
2196
+ ],
2197
+ "properties": {
2198
+ "mode": {
2199
+ "type": "string",
2200
+ "enum": [
2201
+ "matching",
2202
+ "all"
2203
+ ]
1803
2204
  },
1804
- {
1805
- "const": false
2205
+ "market": {
2206
+ "type": "string"
2207
+ },
2208
+ "symbol": {
2209
+ "type": "string"
2210
+ },
2211
+ "markets": {
2212
+ "type": "array",
2213
+ "items": {
2214
+ "type": "string"
2215
+ }
2216
+ },
2217
+ "symbols": {
2218
+ "type": "array",
2219
+ "items": {
2220
+ "type": "string"
2221
+ }
2222
+ },
2223
+ "side": {
2224
+ "type": "string",
2225
+ "enum": [
2226
+ "long",
2227
+ "short"
2228
+ ]
2229
+ },
2230
+ "state": {
2231
+ "type": "string",
2232
+ "enum": [
2233
+ "none",
2234
+ "closed",
2235
+ "absent",
2236
+ "open",
2237
+ "present"
2238
+ ]
2239
+ },
2240
+ "amount": {
2241
+ "type": [
2242
+ "string",
2243
+ "number"
2244
+ ]
2245
+ },
2246
+ "notional": {
2247
+ "type": [
2248
+ "string",
2249
+ "number"
2250
+ ]
2251
+ },
2252
+ "size": {
2253
+ "type": [
2254
+ "string",
2255
+ "number"
2256
+ ]
2257
+ },
2258
+ "leverage": {
2259
+ "type": "number"
2260
+ },
2261
+ "order_type": {
2262
+ "type": "string"
2263
+ },
2264
+ "orderType": {
2265
+ "type": "string"
2266
+ },
2267
+ "limit_price": {
2268
+ "type": "number"
2269
+ },
2270
+ "limitPrice": {
2271
+ "type": "number"
2272
+ },
2273
+ "offset_pct": {
2274
+ "type": "number"
2275
+ },
2276
+ "offsetPct": {
2277
+ "type": "number"
2278
+ },
2279
+ "current_price": {
2280
+ "type": "number"
2281
+ },
2282
+ "currentPrice": {
2283
+ "type": "number"
2284
+ },
2285
+ "max_slippage_bps": {
2286
+ "type": "number"
2287
+ },
2288
+ "maxSlippageBps": {
2289
+ "type": "number"
2290
+ },
2291
+ "close_attempts": {
2292
+ "type": "integer"
2293
+ },
2294
+ "close_retry_delay_ms": {
2295
+ "type": "integer"
2296
+ },
2297
+ "timeout_ms": {
2298
+ "type": "number"
1806
2299
  }
1807
- ],
1808
- "description": "Desired position baseline or false to skip."
2300
+ },
2301
+ "additionalProperties": false
1809
2302
  },
1810
2303
  "orders": {
1811
- "oneOf": [
1812
- {
1813
- "type": "object"
2304
+ "type": [
2305
+ "object",
2306
+ "boolean"
2307
+ ],
2308
+ "properties": {
2309
+ "mode": {
2310
+ "type": "string",
2311
+ "enum": [
2312
+ "matching",
2313
+ "all"
2314
+ ]
1814
2315
  },
1815
- {
1816
- "const": false
2316
+ "market": {
2317
+ "type": "string"
2318
+ },
2319
+ "symbol": {
2320
+ "type": "string"
2321
+ },
2322
+ "markets": {
2323
+ "type": "array",
2324
+ "items": {
2325
+ "type": "string"
2326
+ }
2327
+ },
2328
+ "symbols": {
2329
+ "type": "array",
2330
+ "items": {
2331
+ "type": "string"
2332
+ }
2333
+ },
2334
+ "side": {
2335
+ "type": "string",
2336
+ "enum": [
2337
+ "long",
2338
+ "short"
2339
+ ]
2340
+ },
2341
+ "state": {
2342
+ "type": "string",
2343
+ "enum": [
2344
+ "none",
2345
+ "closed",
2346
+ "absent",
2347
+ "open",
2348
+ "present"
2349
+ ]
2350
+ },
2351
+ "amount": {
2352
+ "type": [
2353
+ "string",
2354
+ "number"
2355
+ ]
2356
+ },
2357
+ "notional": {
2358
+ "type": [
2359
+ "string",
2360
+ "number"
2361
+ ]
2362
+ },
2363
+ "size": {
2364
+ "type": [
2365
+ "string",
2366
+ "number"
2367
+ ]
2368
+ },
2369
+ "leverage": {
2370
+ "type": "number"
2371
+ },
2372
+ "order_type": {
2373
+ "type": "string"
2374
+ },
2375
+ "orderType": {
2376
+ "type": "string"
2377
+ },
2378
+ "limit_price": {
2379
+ "type": "number"
2380
+ },
2381
+ "limitPrice": {
2382
+ "type": "number"
2383
+ },
2384
+ "offset_pct": {
2385
+ "type": "number"
2386
+ },
2387
+ "offsetPct": {
2388
+ "type": "number"
2389
+ },
2390
+ "current_price": {
2391
+ "type": "number"
2392
+ },
2393
+ "currentPrice": {
2394
+ "type": "number"
2395
+ },
2396
+ "max_slippage_bps": {
2397
+ "type": "number"
2398
+ },
2399
+ "maxSlippageBps": {
2400
+ "type": "number"
2401
+ },
2402
+ "close_attempts": {
2403
+ "type": "integer"
2404
+ },
2405
+ "close_retry_delay_ms": {
2406
+ "type": "integer"
2407
+ },
2408
+ "timeout_ms": {
2409
+ "type": "number"
1817
2410
  }
1818
- ],
1819
- "description": "Desired open-order baseline or false to skip."
2411
+ },
2412
+ "additionalProperties": false
1820
2413
  },
1821
2414
  "hud": {
1822
- "type": "object"
2415
+ "type": "object",
2416
+ "properties": {},
2417
+ "additionalProperties": false
1823
2418
  },
1824
2419
  "timeout_ms": {
1825
2420
  "type": "number"
2421
+ },
2422
+ "side": {
2423
+ "type": "string",
2424
+ "enum": [
2425
+ "long",
2426
+ "short"
2427
+ ]
2428
+ },
2429
+ "account_name": {
2430
+ "type": "string"
2431
+ },
2432
+ "readyToTrade": {
2433
+ "type": "boolean"
2434
+ },
2435
+ "balance": {
2436
+ "type": "object",
2437
+ "properties": {
2438
+ "minWithdrawableUsd": {
2439
+ "type": "number"
2440
+ },
2441
+ "minUsd": {
2442
+ "type": "number"
2443
+ },
2444
+ "minSpendableUsd": {
2445
+ "type": "number"
2446
+ }
2447
+ },
2448
+ "additionalProperties": false
2449
+ },
2450
+ "mode": {
2451
+ "type": "string",
2452
+ "enum": [
2453
+ "matching",
2454
+ "all"
2455
+ ]
1826
2456
  }
1827
2457
  },
1828
- "required": [
1829
- "action"
1830
- ]
2458
+ "additionalProperties": false
1831
2459
  },
1832
2460
  "examples": [
1833
2461
  {
@@ -1864,7 +2492,11 @@
1864
2492
  }
1865
2493
  ],
1866
2494
  "proof_effect": "Runs in setup/teardown phase and must be visible in trace.json; proof videos can keep this as trace-only setup.",
1867
- "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof."
2495
+ "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof.",
2496
+ "execution_capabilities": [
2497
+ "app-mutation",
2498
+ "external-mutation"
2499
+ ]
1868
2500
  },
1869
2501
  {
1870
2502
  "name": "metamask.perps.teardown_state",
@@ -1873,9 +2505,6 @@
1873
2505
  "schema": {
1874
2506
  "type": "object",
1875
2507
  "properties": {
1876
- "action": {
1877
- "const": "metamask.perps.teardown_state"
1878
- },
1879
2508
  "profile": {
1880
2509
  "type": "string",
1881
2510
  "description": "Optional profile such as clean_market_testnet, open_position_testnet, open_order_testnet, or provider_mainnet_readonly."
@@ -1913,48 +2542,289 @@
1913
2542
  }
1914
2543
  },
1915
2544
  "page": {
1916
- "oneOf": [
1917
- {
1918
- "type": "string"
1919
- },
1920
- {
1921
- "const": false
1922
- }
2545
+ "description": "Final page target; false skips navigation for teardown.",
2546
+ "type": [
2547
+ "string",
2548
+ "boolean"
1923
2549
  ],
1924
- "description": "Final page target; false skips navigation for teardown."
2550
+ "enum": [
2551
+ "home",
2552
+ "perps",
2553
+ "perps_home",
2554
+ "market",
2555
+ "market_details",
2556
+ "trade",
2557
+ "order",
2558
+ false
2559
+ ]
1925
2560
  },
1926
2561
  "positions": {
1927
- "oneOf": [
1928
- {
1929
- "type": "object"
2562
+ "type": [
2563
+ "object",
2564
+ "boolean"
2565
+ ],
2566
+ "properties": {
2567
+ "mode": {
2568
+ "type": "string",
2569
+ "enum": [
2570
+ "matching",
2571
+ "all"
2572
+ ]
1930
2573
  },
1931
- {
1932
- "const": false
2574
+ "market": {
2575
+ "type": "string"
2576
+ },
2577
+ "symbol": {
2578
+ "type": "string"
2579
+ },
2580
+ "markets": {
2581
+ "type": "array",
2582
+ "items": {
2583
+ "type": "string"
2584
+ }
2585
+ },
2586
+ "symbols": {
2587
+ "type": "array",
2588
+ "items": {
2589
+ "type": "string"
2590
+ }
2591
+ },
2592
+ "side": {
2593
+ "type": "string",
2594
+ "enum": [
2595
+ "long",
2596
+ "short"
2597
+ ]
2598
+ },
2599
+ "state": {
2600
+ "type": "string",
2601
+ "enum": [
2602
+ "none",
2603
+ "closed",
2604
+ "absent",
2605
+ "open",
2606
+ "present"
2607
+ ]
2608
+ },
2609
+ "amount": {
2610
+ "type": [
2611
+ "string",
2612
+ "number"
2613
+ ]
2614
+ },
2615
+ "notional": {
2616
+ "type": [
2617
+ "string",
2618
+ "number"
2619
+ ]
2620
+ },
2621
+ "size": {
2622
+ "type": [
2623
+ "string",
2624
+ "number"
2625
+ ]
2626
+ },
2627
+ "leverage": {
2628
+ "type": "number"
2629
+ },
2630
+ "order_type": {
2631
+ "type": "string"
2632
+ },
2633
+ "orderType": {
2634
+ "type": "string"
2635
+ },
2636
+ "limit_price": {
2637
+ "type": "number"
2638
+ },
2639
+ "limitPrice": {
2640
+ "type": "number"
2641
+ },
2642
+ "offset_pct": {
2643
+ "type": "number"
2644
+ },
2645
+ "offsetPct": {
2646
+ "type": "number"
2647
+ },
2648
+ "current_price": {
2649
+ "type": "number"
2650
+ },
2651
+ "currentPrice": {
2652
+ "type": "number"
2653
+ },
2654
+ "max_slippage_bps": {
2655
+ "type": "number"
2656
+ },
2657
+ "maxSlippageBps": {
2658
+ "type": "number"
2659
+ },
2660
+ "close_attempts": {
2661
+ "type": "integer"
2662
+ },
2663
+ "close_retry_delay_ms": {
2664
+ "type": "integer"
2665
+ },
2666
+ "timeout_ms": {
2667
+ "type": "number"
1933
2668
  }
1934
- ],
1935
- "description": "Desired position baseline or false to skip."
2669
+ },
2670
+ "additionalProperties": false
1936
2671
  },
1937
2672
  "orders": {
1938
- "oneOf": [
1939
- {
1940
- "type": "object"
2673
+ "type": [
2674
+ "object",
2675
+ "boolean"
2676
+ ],
2677
+ "properties": {
2678
+ "mode": {
2679
+ "type": "string",
2680
+ "enum": [
2681
+ "matching",
2682
+ "all"
2683
+ ]
1941
2684
  },
1942
- {
1943
- "const": false
2685
+ "market": {
2686
+ "type": "string"
2687
+ },
2688
+ "symbol": {
2689
+ "type": "string"
2690
+ },
2691
+ "markets": {
2692
+ "type": "array",
2693
+ "items": {
2694
+ "type": "string"
2695
+ }
2696
+ },
2697
+ "symbols": {
2698
+ "type": "array",
2699
+ "items": {
2700
+ "type": "string"
2701
+ }
2702
+ },
2703
+ "side": {
2704
+ "type": "string",
2705
+ "enum": [
2706
+ "long",
2707
+ "short"
2708
+ ]
2709
+ },
2710
+ "state": {
2711
+ "type": "string",
2712
+ "enum": [
2713
+ "none",
2714
+ "closed",
2715
+ "absent",
2716
+ "open",
2717
+ "present"
2718
+ ]
2719
+ },
2720
+ "amount": {
2721
+ "type": [
2722
+ "string",
2723
+ "number"
2724
+ ]
2725
+ },
2726
+ "notional": {
2727
+ "type": [
2728
+ "string",
2729
+ "number"
2730
+ ]
2731
+ },
2732
+ "size": {
2733
+ "type": [
2734
+ "string",
2735
+ "number"
2736
+ ]
2737
+ },
2738
+ "leverage": {
2739
+ "type": "number"
2740
+ },
2741
+ "order_type": {
2742
+ "type": "string"
2743
+ },
2744
+ "orderType": {
2745
+ "type": "string"
2746
+ },
2747
+ "limit_price": {
2748
+ "type": "number"
2749
+ },
2750
+ "limitPrice": {
2751
+ "type": "number"
2752
+ },
2753
+ "offset_pct": {
2754
+ "type": "number"
2755
+ },
2756
+ "offsetPct": {
2757
+ "type": "number"
2758
+ },
2759
+ "current_price": {
2760
+ "type": "number"
2761
+ },
2762
+ "currentPrice": {
2763
+ "type": "number"
2764
+ },
2765
+ "max_slippage_bps": {
2766
+ "type": "number"
2767
+ },
2768
+ "maxSlippageBps": {
2769
+ "type": "number"
2770
+ },
2771
+ "close_attempts": {
2772
+ "type": "integer"
2773
+ },
2774
+ "close_retry_delay_ms": {
2775
+ "type": "integer"
2776
+ },
2777
+ "timeout_ms": {
2778
+ "type": "number"
1944
2779
  }
1945
- ],
1946
- "description": "Desired open-order baseline or false to skip."
2780
+ },
2781
+ "additionalProperties": false
1947
2782
  },
1948
2783
  "hud": {
1949
- "type": "object"
2784
+ "type": "object",
2785
+ "properties": {},
2786
+ "additionalProperties": false
1950
2787
  },
1951
2788
  "timeout_ms": {
1952
2789
  "type": "number"
2790
+ },
2791
+ "side": {
2792
+ "type": "string",
2793
+ "enum": [
2794
+ "long",
2795
+ "short"
2796
+ ]
2797
+ },
2798
+ "account_name": {
2799
+ "type": "string"
2800
+ },
2801
+ "readyToTrade": {
2802
+ "type": "boolean"
2803
+ },
2804
+ "balance": {
2805
+ "type": "object",
2806
+ "properties": {
2807
+ "minWithdrawableUsd": {
2808
+ "type": "number"
2809
+ },
2810
+ "minUsd": {
2811
+ "type": "number"
2812
+ },
2813
+ "minSpendableUsd": {
2814
+ "type": "number"
2815
+ }
2816
+ },
2817
+ "additionalProperties": false
2818
+ },
2819
+ "mode": {
2820
+ "type": "string",
2821
+ "enum": [
2822
+ "matching",
2823
+ "all"
2824
+ ]
1953
2825
  }
1954
2826
  },
1955
- "required": [
1956
- "action"
1957
- ]
2827
+ "additionalProperties": false
1958
2828
  },
1959
2829
  "examples": [
1960
2830
  {
@@ -1982,23 +2852,11 @@
1982
2852
  }
1983
2853
  ],
1984
2854
  "proof_effect": "Runs in setup/teardown phase and must be visible in trace.json; proof videos can keep this as trace-only setup.",
1985
- "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof."
1986
- }
1987
- ],
1988
- "state_refs": [
1989
- {
1990
- "name": "wallet.redacted_state",
1991
- "description": "Redacted wallet state reported by metamask.wallet.read_state."
1992
- },
1993
- {
1994
- "name": "perps.positions",
1995
- "description": "Normalized Perps position state."
1996
- }
1997
- ],
1998
- "pre_conditions": [
1999
- {
2000
- "id": "no-mid-recipe-state-injection",
2001
- "description": "Recipes must drive real supported UI/app/API paths for proof state."
2855
+ "safety_notes": "Composes supported app/API paths and must not mutate React/Redux/MobX/DOM/local storage to fabricate proof.",
2856
+ "execution_capabilities": [
2857
+ "app-mutation",
2858
+ "external-mutation"
2859
+ ]
2002
2860
  }
2003
2861
  ],
2004
2862
  "native_bindings": [
@@ -2146,9 +3004,5 @@
2146
3004
  "action": "metamask.perps.teardown_state",
2147
3005
  "implementation": "@metamask/recipe-runner/extension/perps-domain-dispatcher"
2148
3006
  }
2149
- ],
2150
- "capability_notes": [
2151
- "Perps safety: default to testnet for setup and order/position mutations. Mainnet is read-only unless the user explicitly requests a mainnet mutation."
2152
- ],
2153
- "action_profiles": []
3007
+ ]
2154
3008
  }