waygraph 0.13.6 → 0.14.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 (297) hide show
  1. package/README.md +820 -232
  2. package/assets/stubs/stub.mp4 +0 -0
  3. package/assets/stubs/stub.pdf +32 -0
  4. package/assets/stubs/stub.png +0 -0
  5. package/dist/agent-dive.d.ts +9 -0
  6. package/dist/agent-dive.js +32 -0
  7. package/dist/auto-explore-run.d.ts +20 -2
  8. package/dist/auto-explore-run.js +9 -8
  9. package/dist/auto-explore.js +22 -2
  10. package/dist/auto-session-ipc.d.ts +123 -0
  11. package/dist/auto-session-ipc.js +381 -0
  12. package/dist/auto-session.d.ts +342 -0
  13. package/dist/auto-session.js +760 -0
  14. package/dist/cli.js +417 -51
  15. package/dist/engine.d.ts +40 -1
  16. package/dist/engine.js +82 -0
  17. package/dist/graph.d.ts +18 -0
  18. package/dist/graph.js +65 -2
  19. package/dist/highlights.d.ts +20 -1
  20. package/dist/highlights.js +60 -0
  21. package/dist/index.d.ts +8 -4
  22. package/dist/index.js +5 -3
  23. package/dist/pilot-overlay.d.ts +88 -0
  24. package/dist/pilot-overlay.js +363 -0
  25. package/dist/pilot.d.ts +49 -0
  26. package/dist/pilot.js +53 -0
  27. package/dist/trait.d.ts +22 -0
  28. package/dist/trait.js +60 -3
  29. package/examples/routed-demo/package.json +26 -0
  30. package/examples/routed-demo/playwright.config.ts +20 -0
  31. package/examples/routed-demo/scripts/fixture-server.mjs +37 -0
  32. package/examples/routed-demo/scripts/with-fixture.mjs +70 -0
  33. package/examples/routed-demo/src/flows/routed-demo.flow.ts +12 -0
  34. package/examples/routed-demo/src/pages/dashboard.html +10 -0
  35. package/examples/routed-demo/src/pages/docs.html +8 -0
  36. package/examples/routed-demo/src/routes/(base_app)/dashboard/dashboard.sel.ts +10 -0
  37. package/examples/routed-demo/src/routes/(base_app)/dashboard/methods/click-widget.block.ts +23 -0
  38. package/examples/routed-demo/src/routes/(base_app)/dashboard/nav.block.ts +18 -0
  39. package/examples/routed-demo/src/routes/(base_app)/dashboard/page.block.ts +22 -0
  40. package/examples/routed-demo/src/routes/(external)/docs/docs.sel.ts +7 -0
  41. package/examples/routed-demo/src/routes/(external)/docs/nav.block.ts +21 -0
  42. package/examples/routed-demo/src/routes/(external)/docs/page.block.ts +15 -0
  43. package/examples/routed-demo/src/states/routed-demo.states.ts +10 -0
  44. package/examples/routed-demo/tests/routed-demo.spec.ts +75 -0
  45. package/examples/routed-demo/tsconfig.json +18 -0
  46. package/examples/routed-demo-consumer/package.json +18 -0
  47. package/examples/saucedemo/.waygraph-auto/010dd92b.log +0 -0
  48. package/examples/saucedemo/.waygraph-auto/011ad671.log +0 -0
  49. package/examples/saucedemo/.waygraph-auto/01b45645.log +0 -0
  50. package/examples/saucedemo/.waygraph-auto/03764153.log +0 -0
  51. package/examples/saucedemo/.waygraph-auto/0593587e.log +0 -0
  52. package/examples/saucedemo/.waygraph-auto/06d12823.log +0 -0
  53. package/examples/saucedemo/.waygraph-auto/0b1f5b26.json +8 -0
  54. package/examples/saucedemo/.waygraph-auto/0c00f25f.log +0 -0
  55. package/examples/saucedemo/.waygraph-auto/0e480591.log +0 -0
  56. package/examples/saucedemo/.waygraph-auto/110958b8.log +0 -0
  57. package/examples/saucedemo/.waygraph-auto/138107c1.log +0 -0
  58. package/examples/saucedemo/.waygraph-auto/159d541b.log +0 -0
  59. package/examples/saucedemo/.waygraph-auto/16aa2c01.log +0 -0
  60. package/examples/saucedemo/.waygraph-auto/16c35dd5.log +0 -0
  61. package/examples/saucedemo/.waygraph-auto/1706547d.log +0 -0
  62. package/examples/saucedemo/.waygraph-auto/17537e42.log +0 -0
  63. package/examples/saucedemo/.waygraph-auto/17ede493.log +0 -0
  64. package/examples/saucedemo/.waygraph-auto/1fe2414f.log +0 -0
  65. package/examples/saucedemo/.waygraph-auto/1fe2e3b8.log +0 -0
  66. package/examples/saucedemo/.waygraph-auto/25f68ada.log +0 -0
  67. package/examples/saucedemo/.waygraph-auto/26ca9cd8.log +0 -0
  68. package/examples/saucedemo/.waygraph-auto/2bb6c4e8.log +0 -0
  69. package/examples/saucedemo/.waygraph-auto/2d425004.log +0 -0
  70. package/examples/saucedemo/.waygraph-auto/2f5b3ea6.log +0 -0
  71. package/examples/saucedemo/.waygraph-auto/31bb303d.log +0 -0
  72. package/examples/saucedemo/.waygraph-auto/31f2042f.log +0 -0
  73. package/examples/saucedemo/.waygraph-auto/36551ec1.log +0 -0
  74. package/examples/saucedemo/.waygraph-auto/36a1c274.log +0 -0
  75. package/examples/saucedemo/.waygraph-auto/381aa338.log +0 -0
  76. package/examples/saucedemo/.waygraph-auto/3882bce1.log +0 -0
  77. package/examples/saucedemo/.waygraph-auto/38acd1dc.log +0 -0
  78. package/examples/saucedemo/.waygraph-auto/3b8b77b9.log +0 -0
  79. package/examples/saucedemo/.waygraph-auto/4028d2a2.log +0 -0
  80. package/examples/saucedemo/.waygraph-auto/4168b208.log +0 -0
  81. package/examples/saucedemo/.waygraph-auto/43ab66ce.log +0 -0
  82. package/examples/saucedemo/.waygraph-auto/43f66838.log +0 -0
  83. package/examples/saucedemo/.waygraph-auto/442171d4.log +0 -0
  84. package/examples/saucedemo/.waygraph-auto/453b1ce1.log +0 -0
  85. package/examples/saucedemo/.waygraph-auto/457322bd.log +0 -0
  86. package/examples/saucedemo/.waygraph-auto/46197027.log +0 -0
  87. package/examples/saucedemo/.waygraph-auto/46889468.log +0 -0
  88. package/examples/saucedemo/.waygraph-auto/4890da1c.log +0 -0
  89. package/examples/saucedemo/.waygraph-auto/4893a63b.log +0 -0
  90. package/examples/saucedemo/.waygraph-auto/4af71440.log +0 -0
  91. package/examples/saucedemo/.waygraph-auto/4f7496d1.log +0 -0
  92. package/examples/saucedemo/.waygraph-auto/50e8baaa.log +0 -0
  93. package/examples/saucedemo/.waygraph-auto/52b3a455.log +0 -0
  94. package/examples/saucedemo/.waygraph-auto/532dd9b8.log +0 -0
  95. package/examples/saucedemo/.waygraph-auto/5391ce40.log +0 -0
  96. package/examples/saucedemo/.waygraph-auto/57dbddb3.log +0 -0
  97. package/examples/saucedemo/.waygraph-auto/5897f501.log +0 -0
  98. package/examples/saucedemo/.waygraph-auto/59c0b57d.log +0 -0
  99. package/examples/saucedemo/.waygraph-auto/5b511794.log +0 -0
  100. package/examples/saucedemo/.waygraph-auto/5c47910f.log +0 -0
  101. package/examples/saucedemo/.waygraph-auto/5eda8273.log +0 -0
  102. package/examples/saucedemo/.waygraph-auto/621518ad.log +0 -0
  103. package/examples/saucedemo/.waygraph-auto/632b7118.log +0 -0
  104. package/examples/saucedemo/.waygraph-auto/64314c2b.log +0 -0
  105. package/examples/saucedemo/.waygraph-auto/66516213.log +0 -0
  106. package/examples/saucedemo/.waygraph-auto/66c17079.log +0 -0
  107. package/examples/saucedemo/.waygraph-auto/69bf0b80.log +0 -0
  108. package/examples/saucedemo/.waygraph-auto/69c2347a.log +0 -0
  109. package/examples/saucedemo/.waygraph-auto/6d22bdb0.log +0 -0
  110. package/examples/saucedemo/.waygraph-auto/7103bebb.log +0 -0
  111. package/examples/saucedemo/.waygraph-auto/740c9faa.log +0 -0
  112. package/examples/saucedemo/.waygraph-auto/74872fc5.log +0 -0
  113. package/examples/saucedemo/.waygraph-auto/75846ca9.log +0 -0
  114. package/examples/saucedemo/.waygraph-auto/7626c85b.log +0 -0
  115. package/examples/saucedemo/.waygraph-auto/76308eec.log +0 -0
  116. package/examples/saucedemo/.waygraph-auto/76c1650e.log +0 -0
  117. package/examples/saucedemo/.waygraph-auto/7861f38f.log +0 -0
  118. package/examples/saucedemo/.waygraph-auto/7af224ed.log +0 -0
  119. package/examples/saucedemo/.waygraph-auto/7c3be585.log +0 -0
  120. package/examples/saucedemo/.waygraph-auto/7ed55a2c.log +0 -0
  121. package/examples/saucedemo/.waygraph-auto/80086ba2.log +0 -0
  122. package/examples/saucedemo/.waygraph-auto/80206369.log +0 -0
  123. package/examples/saucedemo/.waygraph-auto/847f1092.log +0 -0
  124. package/examples/saucedemo/.waygraph-auto/8581abfa.log +0 -0
  125. package/examples/saucedemo/.waygraph-auto/87b7e0fd.log +0 -0
  126. package/examples/saucedemo/.waygraph-auto/87c79256.log +0 -0
  127. package/examples/saucedemo/.waygraph-auto/88af2495.log +0 -0
  128. package/examples/saucedemo/.waygraph-auto/8c04a403.log +0 -0
  129. package/examples/saucedemo/.waygraph-auto/8c9ab09e.log +0 -0
  130. package/examples/saucedemo/.waygraph-auto/8cc5db51.log +0 -0
  131. package/examples/saucedemo/.waygraph-auto/8ea3a69c.log +0 -0
  132. package/examples/saucedemo/.waygraph-auto/9150f913.log +0 -0
  133. package/examples/saucedemo/.waygraph-auto/929a4313.log +0 -0
  134. package/examples/saucedemo/.waygraph-auto/95a599e4.log +0 -0
  135. package/examples/saucedemo/.waygraph-auto/96c45350.log +0 -0
  136. package/examples/saucedemo/.waygraph-auto/986030e6.log +0 -0
  137. package/examples/saucedemo/.waygraph-auto/99787abb.log +0 -0
  138. package/examples/saucedemo/.waygraph-auto/99ddb62c.log +0 -0
  139. package/examples/saucedemo/.waygraph-auto/a63a2d20.log +0 -0
  140. package/examples/saucedemo/.waygraph-auto/a78befb7.log +0 -0
  141. package/examples/saucedemo/.waygraph-auto/a84d9b79.log +0 -0
  142. package/examples/saucedemo/.waygraph-auto/a9270890.log +0 -0
  143. package/examples/saucedemo/.waygraph-auto/aa440153.log +0 -0
  144. package/examples/saucedemo/.waygraph-auto/ab7561e0.log +0 -0
  145. package/examples/saucedemo/.waygraph-auto/ac19ae60.log +0 -0
  146. package/examples/saucedemo/.waygraph-auto/ae21ad8e.log +0 -0
  147. package/examples/saucedemo/.waygraph-auto/af09042f.log +0 -0
  148. package/examples/saucedemo/.waygraph-auto/b1684126.log +0 -0
  149. package/examples/saucedemo/.waygraph-auto/b21e9d12.log +0 -0
  150. package/examples/saucedemo/.waygraph-auto/b5a838be.log +0 -0
  151. package/examples/saucedemo/.waygraph-auto/b603549d.log +0 -0
  152. package/examples/saucedemo/.waygraph-auto/b83e6259.log +0 -0
  153. package/examples/saucedemo/.waygraph-auto/b9cb2796.log +0 -0
  154. package/examples/saucedemo/.waygraph-auto/bb0a0018.log +0 -0
  155. package/examples/saucedemo/.waygraph-auto/bc0bdd0e.log +0 -0
  156. package/examples/saucedemo/.waygraph-auto/bc7d4a83.log +0 -0
  157. package/examples/saucedemo/.waygraph-auto/bd8e082a.log +0 -0
  158. package/examples/saucedemo/.waygraph-auto/bea0c93e.log +0 -0
  159. package/examples/saucedemo/.waygraph-auto/bf35201a.log +0 -0
  160. package/examples/saucedemo/.waygraph-auto/bffa3a7a.log +0 -0
  161. package/examples/saucedemo/.waygraph-auto/c4afb8bc.log +0 -0
  162. package/examples/saucedemo/.waygraph-auto/c916a654.log +0 -0
  163. package/examples/saucedemo/.waygraph-auto/c9b8d69e.log +0 -0
  164. package/examples/saucedemo/.waygraph-auto/d02e5e7e.log +0 -0
  165. package/examples/saucedemo/.waygraph-auto/d09aff71.log +0 -0
  166. package/examples/saucedemo/.waygraph-auto/d0e41396.log +0 -0
  167. package/examples/saucedemo/.waygraph-auto/d1370d76.log +0 -0
  168. package/examples/saucedemo/.waygraph-auto/d3714ae4.log +0 -0
  169. package/examples/saucedemo/.waygraph-auto/da0685dc.log +0 -0
  170. package/examples/saucedemo/.waygraph-auto/da894ad8.log +0 -0
  171. package/examples/saucedemo/.waygraph-auto/daa2f922.log +0 -0
  172. package/examples/saucedemo/.waygraph-auto/daefb614.log +0 -0
  173. package/examples/saucedemo/.waygraph-auto/db97e833.log +0 -0
  174. package/examples/saucedemo/.waygraph-auto/e101d19a.log +0 -0
  175. package/examples/saucedemo/.waygraph-auto/e207c989.log +0 -0
  176. package/examples/saucedemo/.waygraph-auto/e633e545.log +0 -0
  177. package/examples/saucedemo/.waygraph-auto/e69aec68.log +0 -0
  178. package/examples/saucedemo/.waygraph-auto/e70e2d06.log +0 -0
  179. package/examples/saucedemo/.waygraph-auto/ebad3410.log +0 -0
  180. package/examples/saucedemo/.waygraph-auto/ecbb0966.log +0 -0
  181. package/examples/saucedemo/.waygraph-auto/ecdee753.log +0 -0
  182. package/examples/saucedemo/.waygraph-auto/ed888783.log +0 -0
  183. package/examples/saucedemo/.waygraph-auto/ed931dac.log +0 -0
  184. package/examples/saucedemo/.waygraph-auto/ef655a3f.log +0 -0
  185. package/examples/saucedemo/.waygraph-auto/f3c29b21.log +0 -0
  186. package/examples/saucedemo/.waygraph-auto/f53f7cfc.log +0 -0
  187. package/examples/saucedemo/.waygraph-auto/f5ad5830.log +0 -0
  188. package/examples/saucedemo/.waygraph-auto/faed36dd.log +0 -0
  189. package/examples/saucedemo/docs/CONVENTION.md +25 -5
  190. package/examples/saucedemo/docs/HELPERS.md +15 -4
  191. package/examples/saucedemo/scripts/capture-wrong-login.mjs +8 -2
  192. package/examples/saucedemo/scripts/prove-wrong-login-runtime.mjs +8 -2
  193. package/examples/saucedemo/src/blocks/SITE-MAP.md +14 -3
  194. package/examples/saucedemo/src/blocks/saucedemo-web/NAV.md +15 -1
  195. package/examples/saucedemo/src/blocks/saucedemo-web/cart/NAV.md +1 -0
  196. package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.sel.ts +9 -0
  197. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +3 -2
  198. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts +2 -1
  199. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/NAV.md +6 -1
  200. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/checkout-info.sel.ts +11 -0
  201. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts +22 -0
  202. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts +21 -0
  203. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts +21 -0
  204. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts +6 -7
  205. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/NAV.md +2 -0
  206. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/checkout-overview.sel.ts +9 -0
  207. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +7 -6
  208. package/examples/saucedemo/src/blocks/saucedemo-web/ff-owner-auth.block.ts +7 -2
  209. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/NAV.md +1 -0
  210. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/item-detail.sel.ts +9 -0
  211. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +2 -1
  212. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +2 -1
  213. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts +2 -1
  214. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +51 -0
  215. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +69 -0
  216. package/examples/saucedemo/src/blocks/saucedemo-web/methods/login.sel.ts +13 -0
  217. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +4 -4
  218. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +13 -51
  219. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-logout.method.block.ts +4 -3
  220. package/examples/saucedemo/src/blocks/saucedemo-web/nav-login.block.ts +14 -6
  221. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +15 -0
  222. package/examples/saucedemo/src/flows/checkout.flow.ts +20 -2
  223. package/examples/saucedemo/src/flows/device-showcase.flow.ts +4 -0
  224. package/examples/saucedemo/src/flows/login.flow.ts +3 -1
  225. package/examples/saucedemo/src/flows/shop.flow.ts +6 -0
  226. package/examples/saucedemo/src/flows/smoke.flow.ts +13 -7
  227. package/examples/saucedemo/src/flows/stub-on-error.flow.ts +4 -2
  228. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +6 -2
  229. package/examples/saucedemo/tests/flow-block-verify.spec.ts +3 -3
  230. package/examples/saucedemo/tests/submit-login-branch.spec.ts +5 -3
  231. package/package.json +2 -1
  232. package/templates/agents/waygraph-author.agent.md +43 -5
  233. package/templates/agents/waygraph-planner.agent.md +33 -2
  234. package/templates/quickstart/src/blocks/saucedemo-web/NAV.md +15 -1
  235. package/templates/quickstart/src/blocks/saucedemo-web/cart/NAV.md +1 -0
  236. package/templates/quickstart/src/blocks/saucedemo-web/cart/cart.sel.ts +9 -0
  237. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-cart.block.ts +19 -15
  238. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts +2 -1
  239. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/NAV.md +6 -1
  240. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/checkout-info.sel.ts +11 -0
  241. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts +22 -0
  242. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts +21 -0
  243. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts +21 -0
  244. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts +6 -7
  245. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/NAV.md +2 -0
  246. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/checkout-overview.sel.ts +9 -0
  247. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +7 -6
  248. package/templates/quickstart/src/blocks/saucedemo-web/ff-owner-auth.block.ts +7 -2
  249. package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +24 -16
  250. package/templates/quickstart/src/blocks/saucedemo-web/inventory/nav-item-detail.block.ts +3 -3
  251. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/NAV.md +1 -0
  252. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/item-detail.sel.ts +9 -0
  253. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +2 -1
  254. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +2 -1
  255. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts +2 -1
  256. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +51 -0
  257. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +69 -0
  258. package/templates/quickstart/src/blocks/saucedemo-web/methods/login.sel.ts +13 -0
  259. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +4 -4
  260. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +35 -63
  261. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-logout.method.block.ts +4 -3
  262. package/templates/quickstart/src/blocks/saucedemo-web/nav-login.block.ts +14 -6
  263. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +15 -0
  264. package/templates/quickstart/src/flows/checkout.flow.ts +29 -2
  265. package/templates/quickstart/src/flows/device-showcase.flow.ts +4 -0
  266. package/templates/quickstart/src/flows/login.flow.ts +3 -1
  267. package/templates/quickstart/src/flows/shop.flow.ts +6 -0
  268. package/templates/quickstart/src/flows/smoke.flow.ts +13 -7
  269. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +6 -2
  270. package/templates/quickstart/tests/chain-flow.spec.ts +8 -1
  271. package/templates/scaffold/STRUCTURE.md +23 -3
  272. package/templates/scaffold/src/blocks/demo-external/mailpit/mailpit-inbox.page.block.ts +30 -0
  273. package/templates/scaffold/src/blocks/demo-external/mailpit/mailpit-sel.ts +5 -0
  274. package/templates/scaffold/src/blocks/demo-external/mailpit/methods/assert-email-content.method.block.ts +48 -0
  275. package/templates/scaffold/src/blocks/demo-external/mailpit/methods/assert-email-received.method.block.ts +49 -0
  276. package/templates/scaffold/src/blocks/demo-external/mailpit/methods/extract-email-link.method.block.ts +41 -0
  277. package/templates/scaffold/src/blocks/demo-external/mailpit/methods/open-message.method.block.ts +39 -0
  278. package/templates/scaffold/src/blocks/demo-external/mailpit/nav-mailpit-inbox.block.ts +20 -0
  279. package/templates/scaffold/src/blocks/demo-web/demo-sel.ts +1 -0
  280. package/templates/scaffold/src/blocks/demo-web/docs.html +8 -0
  281. package/templates/scaffold/src/blocks/demo-web/home.html +4 -0
  282. package/templates/scaffold/src/blocks/demo-web/methods/assert-hello.method.block.ts +34 -38
  283. package/templates/scaffold/src/blocks/demo-web/methods/assert-item-added.method.block.ts +38 -0
  284. package/templates/scaffold/src/blocks/demo-web/nav-verification-link.block.ts +34 -0
  285. package/templates/scaffold/src/flows/mail-verify.flow.ts +85 -0
  286. package/templates/scaffold/src/flows/routes-demo.flow.ts +14 -0
  287. package/templates/scaffold/src/flows/shop.flow.ts +19 -2
  288. package/templates/scaffold/src/routes/(external)/docs/docs.sel.ts +7 -0
  289. package/templates/scaffold/src/routes/(external)/docs/nav.block.ts +21 -0
  290. package/templates/scaffold/src/routes/(external)/docs/page.block.ts +15 -0
  291. package/templates/scaffold/src/states/demo.mem-keys.ts +25 -1
  292. package/templates/scaffold/src/states/demo.states.ts +13 -0
  293. package/templates/scaffold/test-results/.last-run.json +4 -0
  294. package/templates/scaffold/tests/mail-verify.spec.ts +191 -0
  295. package/templates/skills/waygraph-blind-pilot.skill.md +61 -0
  296. package/templates/skills/waygraph-pilot.skill.md +108 -0
  297. package/templates/quickstart/README-saucedemo-commands.md +0 -72
@@ -0,0 +1,363 @@
1
+ import { WAYGRAPH_RING_CSS } from "./highlights.js";
2
+ // Real, direct user request: this overlay's own colors were an improvised
3
+ // purple/white scheme (#a78bfa / #1a1033) that didn't match anything else -
4
+ // "our design here seems like barely shares with the ui on the stepper
5
+ // design... looks a bit different and out of sync". These are the SAME
6
+ // colors `cli.ts`'s own demo panel/ring already use (#7C3AED primary
7
+ // purple, the dark rgba(20,10,40,.94) panel background + white text,
8
+ // #c9a6ff for a secondary purple accent on that dark background - all
9
+ // copied verbatim from cli.ts's own `#wg-panel`/`RING_CSS`, not
10
+ // re-invented) - genuinely the same visual language, not just the ring CSS
11
+ // this file already shared via WAYGRAPH_RING_CSS.
12
+ const OVERLAY_CSS = `
13
+ #wg-pilot-overlay {
14
+ position: fixed; z-index: 2147483000; bottom: 12px; right: 12px;
15
+ font: 12px/1.4 ui-monospace, "SF Mono", Consolas, monospace;
16
+ color: #fff; pointer-events: auto;
17
+ }
18
+ #wg-pilot-badge {
19
+ background: #7C3AED; color: #fff; padding: 6px 10px; border-radius: 8px;
20
+ box-shadow: 0 2px 8px rgba(0,0,0,0.25); cursor: pointer; white-space: nowrap;
21
+ font-weight: 600; user-select: none;
22
+ }
23
+ #wg-pilot-panel {
24
+ display: none; margin-top: 6px; max-width: 420px; max-height: 320px;
25
+ overflow-y: auto; background: rgba(20,10,40,.94); color: #fff; border-radius: 8px;
26
+ box-shadow: 0 2px 8px rgba(0,0,0,0.25); padding: 8px 10px;
27
+ }
28
+ /* The graph tab renders a real Mermaid flowchart, which needs more room than
29
+ the flat-list "Here" tab and its own scroll - real, direct user request:
30
+ "a 2d version. left to right stuff. maybe mermaid?" replacing the original
31
+ flat indented-text tree. Widened+its own scrollbox rather than growing
32
+ the shared panel unconditionally, so "Here" stays compact. */
33
+ #wg-pilot-panel:has(#wg-pilot-panel-graph:not([style*="display: none"])) {
34
+ max-width: 640px; max-height: 460px;
35
+ }
36
+ #wg-pilot-panel-graph { overflow: auto; }
37
+ #wg-pilot-panel-graph svg { max-width: none; }
38
+ #wg-pilot-panel-graph .wg-pilot-graph-note { color: #999; font-style: italic; font-size: 11px; padding: 4px 2px 0; }
39
+ #wg-pilot-panel.wg-pilot-open { display: block; }
40
+ #wg-pilot-panel-toggle { display: flex; gap: 6px; margin-bottom: 8px; }
41
+ #wg-pilot-panel-toggle button {
42
+ background: transparent; color: #bbb; border: 1px solid rgba(255,255,255,.2);
43
+ border-radius: 6px; padding: 3px 8px; font: inherit; cursor: pointer;
44
+ }
45
+ #wg-pilot-panel-toggle button.wg-pilot-tab-active { background: #7C3AED; color: #fff; border-color: #7C3AED; }
46
+ #wg-pilot-panel .wg-pilot-section { font-weight: 700; margin: 6px 0 2px; color: #c9a6ff; }
47
+ #wg-pilot-panel .wg-pilot-edge { padding: 2px 0; border-bottom: 1px solid rgba(255,255,255,.12); }
48
+ #wg-pilot-panel .wg-pilot-kind { color: #c9a6ff; font-weight: 600; }
49
+ #wg-pilot-panel .wg-pilot-desc { color: #bbb; }
50
+ #wg-pilot-panel .wg-pilot-empty { color: #999; font-style: italic; }
51
+ #wg-pilot-panel-graph .wg-pilot-graph-node { margin: 6px 0; }
52
+ #wg-pilot-panel-graph .wg-pilot-graph-checkpoint { color: #c9a6ff; font-weight: 700; }
53
+ #wg-pilot-panel-graph .wg-pilot-graph-edge { padding: 2px 0 2px 14px; border-left: 2px solid rgba(255,255,255,.15); font-size: 11px; }
54
+ #wg-pilot-panel-graph .wg-pilot-graph-edge .wg-pilot-kind { color: #c9a6ff; }
55
+ #wg-pilot-activity {
56
+ position: fixed; z-index: 2147483000; bottom: 12px; left: 12px;
57
+ font: 12px/1.4 ui-monospace, "SF Mono", Consolas, monospace;
58
+ background: rgba(20,10,40,.94); color: #fff; padding: 6px 10px; border-radius: 8px;
59
+ box-shadow: 0 2px 8px rgba(0,0,0,0.25); white-space: nowrap; pointer-events: none;
60
+ opacity: 0; transform: translateY(4px); transition: opacity 0.15s, transform 0.15s;
61
+ }
62
+ #wg-pilot-activity.wg-pilot-toast-show { opacity: 1; transform: translateY(0); }
63
+ ` + WAYGRAPH_RING_CSS;
64
+ /** Idempotent - safe to call before every update, matching narrate mode's own precedent. */
65
+ async function ensureInstalled(page) {
66
+ await page.addStyleTag({ content: OVERLAY_CSS }).catch(() => { });
67
+ await page.evaluate(() => {
68
+ if (document.getElementById("wg-pilot-overlay"))
69
+ return;
70
+ const root = document.createElement("div");
71
+ root.id = "wg-pilot-overlay";
72
+ // Real, direct user request: a way to see the WHOLE project's graph
73
+ // (every Checkpoint/Block, not just what's reachable from the current
74
+ // page) directly in the overlay, "like a node tree" - toggled inside
75
+ // the same panel rather than a second popup, so there's one place to
76
+ // look, not two.
77
+ root.innerHTML =
78
+ '<div id="wg-pilot-badge"></div>' +
79
+ '<div id="wg-pilot-panel">' +
80
+ '<div id="wg-pilot-panel-toggle"><button id="wg-pilot-view-current" class="wg-pilot-tab-active">Here</button>' +
81
+ '<button id="wg-pilot-view-graph">All nodes</button></div>' +
82
+ '<div id="wg-pilot-panel-current"></div>' +
83
+ '<div id="wg-pilot-panel-graph" style="display:none"></div>' +
84
+ "</div>";
85
+ document.documentElement.appendChild(root);
86
+ document.getElementById("wg-pilot-badge").addEventListener("click", () => {
87
+ document.getElementById("wg-pilot-panel").classList.toggle("wg-pilot-open");
88
+ });
89
+ document.getElementById("wg-pilot-view-current").addEventListener("click", () => {
90
+ document.getElementById("wg-pilot-view-current").classList.add("wg-pilot-tab-active");
91
+ document.getElementById("wg-pilot-view-graph").classList.remove("wg-pilot-tab-active");
92
+ document.getElementById("wg-pilot-panel-current").style.display = "";
93
+ document.getElementById("wg-pilot-panel-graph").style.display = "none";
94
+ });
95
+ document.getElementById("wg-pilot-view-graph").addEventListener("click", () => {
96
+ document.getElementById("wg-pilot-view-graph").classList.add("wg-pilot-tab-active");
97
+ document.getElementById("wg-pilot-view-current").classList.remove("wg-pilot-tab-active");
98
+ document.getElementById("wg-pilot-panel-graph").style.display = "";
99
+ document.getElementById("wg-pilot-panel-current").style.display = "none";
100
+ });
101
+ const toast = document.createElement("div");
102
+ toast.id = "wg-pilot-activity";
103
+ document.documentElement.appendChild(toast);
104
+ const ring = document.createElement("div");
105
+ ring.id = "wg-ring";
106
+ const ringLabel = document.createElement("div");
107
+ ringLabel.id = "wg-ring-label";
108
+ document.documentElement.appendChild(ring);
109
+ document.documentElement.appendChild(ringLabel);
110
+ }).catch(() => { });
111
+ }
112
+ /**
113
+ * Bottom-left transient toast, separate from the persistent bottom-right
114
+ * badge - real, direct user request: something that visibly flags "a
115
+ * command is running" / "the map just changed" distinct from the
116
+ * always-on Checkpoint badge. Self-clearing (CSS fade after a timeout),
117
+ * not tied to the next `currentMenu()` snapshot the way the badge is,
118
+ * since some callers (rawClick/rawType/rawGoto/reloadLibrary/applyPick)
119
+ * only call `currentMenu()` again once the whole operation - including
120
+ * this toast's own lifetime - should already be over.
121
+ */
122
+ export async function showPilotActivity(page, text) {
123
+ await ensureInstalled(page);
124
+ await page
125
+ .evaluate((text) => {
126
+ const toast = document.getElementById("wg-pilot-activity");
127
+ if (!toast)
128
+ return;
129
+ toast.textContent = text;
130
+ toast.classList.add("wg-pilot-toast-show");
131
+ const w = window;
132
+ if (w.__wgPilotToastTimer)
133
+ clearTimeout(w.__wgPilotToastTimer);
134
+ w.__wgPilotToastTimer = setTimeout(() => {
135
+ toast.classList.remove("wg-pilot-toast-show");
136
+ }, 2500);
137
+ }, text)
138
+ .catch(() => { });
139
+ }
140
+ /**
141
+ * "browser-use"-style vision box: a ring drawn around the exact element
142
+ * `auto dom --selector <sel>` just inspected, so a human watching a headful
143
+ * Pilot session can see what the agent is actually looking at, not just a
144
+ * JSON blob in a terminal. Real, direct user request.
145
+ *
146
+ * Reuses the real `#wg-ring`/`#wg-ring-label` elements + `WAYGRAPH_RING_CSS`
147
+ * (see `highlights.ts`) - the exact same visual language `cli.ts`'s own demo
148
+ * stepper paints rings with, not a separate look-alike. Still a purpose-built
149
+ * renderer here (position/show/hide only, via plain `getBoundingClientRect` -
150
+ * no camera zoom/spotlight/todo-dock/pacing), since that whole engine still
151
+ * lives in `cli.ts`, which this file continues to avoid importing (see this
152
+ * file's own header). Auto-fades a few real seconds after showing (or
153
+ * sooner if explicitly replaced/hidden first) - tuned live twice: an
154
+ * original 2.5s fade read as gone before it could be seen, a since-tried
155
+ * "stay up indefinitely" alternative read as lingering clutter instead.
156
+ *
157
+ * `tone` defaults to "auto" (gray) - real, direct user request: "gray is
158
+ * waygraph playwright run. orange is dom check" - a real Block run or a
159
+ * raw click/type/upload IS a real Playwright-driven action (`auto` was
160
+ * always documented as meaning exactly that), while a pure `auto dom` read
161
+ * touches nothing, so it gets its own "orange" tone instead.
162
+ */
163
+ export async function showPilotVision(page, selector, label, tone = "auto") {
164
+ await ensureInstalled(page);
165
+ await page
166
+ .evaluate(({ selector, label, tone }) => {
167
+ const ring = document.getElementById("wg-ring");
168
+ const tag = document.getElementById("wg-ring-label");
169
+ if (!ring || !tag)
170
+ return;
171
+ const w = window;
172
+ if (w.__wgVisionHideTimer)
173
+ clearTimeout(w.__wgVisionHideTimer);
174
+ const el = document.querySelector(selector);
175
+ if (!el) {
176
+ ring.style.opacity = "0";
177
+ tag.style.opacity = "0";
178
+ return;
179
+ }
180
+ const rect = el.getBoundingClientRect();
181
+ if (rect.width === 0 && rect.height === 0) {
182
+ ring.style.opacity = "0";
183
+ tag.style.opacity = "0";
184
+ return;
185
+ }
186
+ ring.dataset.tone = tone;
187
+ ring.dataset.size = "md";
188
+ tag.dataset.tone = tone;
189
+ tag.dataset.size = "md";
190
+ tag.dataset.weight = "normal";
191
+ ring.style.left = `${rect.left - 3}px`;
192
+ ring.style.top = `${rect.top - 3}px`;
193
+ ring.style.width = `${rect.width + 6}px`;
194
+ ring.style.height = `${rect.height + 6}px`;
195
+ tag.textContent = label;
196
+ tag.style.left = `${Math.max(0, rect.left)}px`;
197
+ tag.style.top = `${Math.max(0, rect.top - 24)}px`;
198
+ ring.style.opacity = "1";
199
+ tag.style.opacity = "1";
200
+ // Real, direct user correction: this used to be a 30s "long
201
+ // fallback" (stay up until explicitly replaced) - after actually
202
+ // watching it, the user wants it to auto-clear after a few real
203
+ // seconds instead, not linger. Short enough to still read as "just
204
+ // happened", not so short it's gone before you look (that was the
205
+ // ORIGINAL complaint this session, at 2.5s - this is deliberately
206
+ // in between).
207
+ w.__wgVisionHideTimer = setTimeout(() => {
208
+ ring.style.opacity = "0";
209
+ tag.style.opacity = "0";
210
+ }, 4_000);
211
+ }, { selector, label, tone })
212
+ .catch(() => { });
213
+ }
214
+ /**
215
+ * Refreshes the badge text and the (possibly-collapsed) panel's two tabs
216
+ * ("Here" - current live menu; "All nodes" - whole project graph as a
217
+ * tree). Never throws - a page mid-navigation when this fires is a real,
218
+ * expected timing case, not an error worth failing the caller's own real
219
+ * operation over.
220
+ */
221
+ export async function updatePilotOverlay(page, info) {
222
+ await ensureInstalled(page);
223
+ await page
224
+ .evaluate(async ({ sessionId, snapshot, graph }) => {
225
+ const badge = document.getElementById("wg-pilot-badge");
226
+ const current = document.getElementById("wg-pilot-panel-current");
227
+ const graphPanel = document.getElementById("wg-pilot-panel-graph");
228
+ if (!badge || !current || !graphPanel)
229
+ return;
230
+ const idPart = sessionId ? `session ${sessionId}` : "no session id";
231
+ badge.textContent = `Waygraph Pilot - ${idPart} - ${snapshot.here ?? "(unknown)"}`;
232
+ const totalEdges = snapshot.sections.reduce((n, s) => n + s.edges.length, 0);
233
+ if (totalEdges === 0) {
234
+ current.innerHTML = '<div class="wg-pilot-empty">No moves from here right now.</div>';
235
+ }
236
+ else {
237
+ current.innerHTML = snapshot.sections
238
+ .map((section) => {
239
+ const rows = section.edges
240
+ .map((edge) => {
241
+ const desc = edge.description ? ` - ${edge.description}` : "";
242
+ const label = edge.label ? ` (${edge.label})` : "";
243
+ return `<div class="wg-pilot-edge">[${edge.index}] <span class="wg-pilot-kind">${edge.kind}</span> ${edge.block}${label} -&gt; ${edge.to}${desc}</div>`;
244
+ })
245
+ .join("");
246
+ return `<div class="wg-pilot-section">${section.title}</div>${rows}`;
247
+ })
248
+ .join("");
249
+ }
250
+ if (!graph || graph.nodes.length === 0) {
251
+ graphPanel.innerHTML = '<div class="wg-pilot-empty">No graph data yet.</div>';
252
+ return;
253
+ }
254
+ // Flat indented-text tree - the ORIGINAL "All nodes" rendering.
255
+ // Real, direct user correction: "i also dont like the all node
256
+ // stuff. ther should be a 2d version" - kept only as a fallback for
257
+ // when Mermaid genuinely can't load (e.g. offline), not the primary
258
+ // rendering anymore.
259
+ const renderTextTree = () => {
260
+ graphPanel.innerHTML = graph.nodes
261
+ .map((node) => {
262
+ const outgoing = graph.edges.filter((e) => e.from === node.checkpoint || e.from === "*");
263
+ const rows = outgoing.length
264
+ ? outgoing
265
+ .map((e) => `<div class="wg-pilot-graph-edge"><span class="wg-pilot-kind">${e.kind}</span> ${e.block} -&gt; ${e.to}</div>`)
266
+ .join("")
267
+ : '<div class="wg-pilot-graph-edge wg-pilot-empty">(no outgoing edges)</div>';
268
+ const here = node.checkpoint === snapshot.here ? " (here)" : "";
269
+ return `<div class="wg-pilot-graph-node"><div class="wg-pilot-graph-checkpoint">${node.checkpoint}${here}</div>${rows}</div>`;
270
+ })
271
+ .join("");
272
+ };
273
+ try {
274
+ const w = window;
275
+ if (!w.mermaid) {
276
+ if (!w.__wgMermaidLoadPromise) {
277
+ w.__wgMermaidLoadPromise = new Promise((resolve, reject) => {
278
+ const script = document.createElement("script");
279
+ script.src = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js";
280
+ script.onload = () => resolve(undefined);
281
+ script.onerror = () => reject(new Error("mermaid script failed to load"));
282
+ document.head.appendChild(script);
283
+ });
284
+ }
285
+ await w.__wgMermaidLoadPromise;
286
+ }
287
+ if (!w.mermaid)
288
+ throw new Error("mermaid did not attach to window after load");
289
+ // Real, direct user request: "left to right stuff. maybe mermaid?
290
+ // with square ish arrows???" - LR layout, linear (non-curvy) edges.
291
+ w.mermaid.initialize({ startOnLoad: false, theme: "dark", securityLevel: "loose", flowchart: { curve: "linear" } });
292
+ const sanitize = (s) => s.replace(/[^A-Za-z0-9_]/g, "_");
293
+ // Every Nav Block gets a `from:"*"` wildcard edge for pathfinding
294
+ // (see auto-explore.ts's own isUrlNav comment). Real, screenshot-
295
+ // confirmed finding, TWICE over:
296
+ // 1. Collapsing all of these into one shared "Anywhere" hub node
297
+ // still broke the layout - ~9 of 10 Checkpoints have their own
298
+ // nav edge, so nearly every node ends up one hop from that hub
299
+ // and dagre stacks them in a single rank/column ("it dones't
300
+ // look left to right").
301
+ // 2. Dropping them entirely (no edges, just a footnote) fixed the
302
+ // layout but then hid real connectivity - a node like Settings
303
+ // that has ONLY a nav edge looked like a disconnected orphan
304
+ // ("the all nodes doesn't even show the navs block... whre is
305
+ // settings there").
306
+ // Neither extreme is right: a wildcard nav edge isn't really a
307
+ // flow TRANSITION (it doesn't come from any one specific screen,
308
+ // it's global infrastructure - the persistent sidebar), so it
309
+ // doesn't belong in the layout-driving edge set at all, but the
310
+ // fact that a node is directly nav-reachable is still real
311
+ // information worth showing. Marked on the node's own label
312
+ // instead of as a graph edge - visible on every affected node,
313
+ // adds zero layout-warping connectivity.
314
+ const nonWildcard = graph.edges.filter((e) => e.from !== "*");
315
+ const navReachable = new Set(graph.edges.filter((e) => e.from === "*").map((e) => e.to));
316
+ // Real, direct user finding (screenshot-confirmed): a self-loop
317
+ // edge (an action that stays on the same Checkpoint, e.g.
318
+ // "fill-email -> SignIn") renders as a spiral squiggle Mermaid
319
+ // draws back into the same node - with several per node (every
320
+ // fill-* / select-* action), these dominated the diagram and
321
+ // broke the left-to-right layout dagre otherwise produces cleanly
322
+ // for a real DAG of inter-Checkpoint transitions. Self-loops add
323
+ // no flow information anyway (the "Here" tab already lists every
324
+ // action from wherever you are) - excluded from the graph itself,
325
+ // kept only as a "(+N)" count on the node label so the count
326
+ // isn't silently lost.
327
+ const selfLoopCounts = new Map();
328
+ for (const e of nonWildcard) {
329
+ if (e.from === e.to)
330
+ selfLoopCounts.set(e.from, (selfLoopCounts.get(e.from) ?? 0) + 1);
331
+ }
332
+ const specific = nonWildcard.filter((e) => e.from !== e.to);
333
+ const lines = ["graph LR"];
334
+ for (const node of graph.nodes) {
335
+ const id = sanitize(node.checkpoint);
336
+ const here = node.checkpoint === snapshot.here;
337
+ const loops = selfLoopCounts.get(node.checkpoint);
338
+ const nav = navReachable.has(node.checkpoint) ? " [nav]" : "";
339
+ const suffix = `${here ? " (here)" : ""}${loops ? ` (+${loops})` : ""}${nav}`;
340
+ lines.push(` ${id}["${node.checkpoint}${suffix}"]`);
341
+ if (here)
342
+ lines.push(` style ${id} stroke:#7C3AED,stroke-width:3px`);
343
+ }
344
+ for (const e of specific) {
345
+ lines.push(` ${sanitize(e.from)} -->|${e.block}| ${sanitize(e.to)}`);
346
+ }
347
+ const { svg } = await w.mermaid.render(`wg-mermaid-${Date.now()}`, lines.join("\n"));
348
+ graphPanel.innerHTML =
349
+ svg + `<div class="wg-pilot-graph-note">[nav] = also directly reachable via sidebar/URL from anywhere (not drawn as an edge - see the "Here" tab for what's live on this page).</div>`;
350
+ // Real, direct user request: "i hope it is zoomed to the current
351
+ // node" - the diagram is usually bigger than the panel, so scroll
352
+ // (not zoom - the SVG itself stays real size, legible) the "here"
353
+ // node into view instead of leaving it wherever dagre placed it.
354
+ const hereId = snapshot.here ? sanitize(snapshot.here) : null;
355
+ const hereEl = hereId ? graphPanel.querySelector(`[id^="flowchart-${hereId}-"]`) : null;
356
+ hereEl?.scrollIntoView({ block: "center", inline: "center" });
357
+ }
358
+ catch {
359
+ renderTextTree();
360
+ }
361
+ }, { sessionId: info.sessionId, snapshot: info.snapshot, graph: info.graph ?? null })
362
+ .catch(() => { });
363
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Waygraph Pilot: the one bootstrap call an agent needs to start driving a
3
+ * real, persistent, inspectable browser session toward a natural-language
4
+ * goal it plans and executes itself.
5
+ *
6
+ * Deliberately NOT a resolver that maps one ask to one Block
7
+ * (`resolveAsk`/`pilotNarrate`/`pilotAct`, the v1 shape this replaced - see
8
+ * git tag `waygraph-pilot-v1-logs-prettified` for that history). Real
9
+ * requests are multi-step ("log in and buy the backpack for me", "make a
10
+ * signature draft request") and need an agent that plans a whole sequence
11
+ * against the graph, not a single best-matching edge. `pilotStart` does not
12
+ * plan, resolve, narrate, or act on anything - it only combines two
13
+ * already-proven, separately-existing primitives into one payload:
14
+ *
15
+ * - `spawnDetachedSession` (same mechanism as `auto --cli --detach`) - a
16
+ * real, persistent, visible-if-headful Playwright session an agent can
17
+ * keep driving across many calls, not a fresh session per request.
18
+ * - `discoverGraph` (same mechanism as `waygraph graph`) - the WHOLE
19
+ * project's Checkpoint/edge/description graph, not just what's reachable
20
+ * from the session's current position, because planning a multi-step
21
+ * route needs to see steps ahead, not just the next one.
22
+ *
23
+ * Driving the session afterward - `auto send <id> "<pick>"`, `auto status
24
+ * <id>`, `auto dom <id>`, `auto trace <id>` - is left entirely to whatever
25
+ * agent is holding it. All four already existed before this file did; see
26
+ * openspec/changes/waygraph-pilot/design.md for the worked example (a real
27
+ * login + add-to-cart + checkout run against examples/saucedemo, driven by
28
+ * hand-picking indices from each status response) that proved this needs no
29
+ * new execution primitive.
30
+ */
31
+ import { type WaygraphGraph } from "./graph.js";
32
+ import type { AutoSessionInit, SessionSnapshot } from "./auto-session.js";
33
+ export interface PilotStartResult {
34
+ sessionId: string;
35
+ socketPath: string;
36
+ headless: boolean;
37
+ /** The whole project's Block graph - every Checkpoint/edge/description, not just what's reachable right now. */
38
+ graph: WaygraphGraph;
39
+ /** The session's starting position, or `null` if the very first `status` read failed. */
40
+ snapshot: SessionSnapshot | null;
41
+ }
42
+ /**
43
+ * Starts a detached session and reads back its own graph + starting
44
+ * snapshot in one call, so an agent bootstraps with everything it needs
45
+ * (a live session it can keep driving, and the full map of what exists) in
46
+ * one round trip instead of separately knowing to chain `auto --cli
47
+ * --detach` and `waygraph graph` itself.
48
+ */
49
+ export declare function pilotStart(init: AutoSessionInit): Promise<PilotStartResult>;
package/dist/pilot.js ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Waygraph Pilot: the one bootstrap call an agent needs to start driving a
3
+ * real, persistent, inspectable browser session toward a natural-language
4
+ * goal it plans and executes itself.
5
+ *
6
+ * Deliberately NOT a resolver that maps one ask to one Block
7
+ * (`resolveAsk`/`pilotNarrate`/`pilotAct`, the v1 shape this replaced - see
8
+ * git tag `waygraph-pilot-v1-logs-prettified` for that history). Real
9
+ * requests are multi-step ("log in and buy the backpack for me", "make a
10
+ * signature draft request") and need an agent that plans a whole sequence
11
+ * against the graph, not a single best-matching edge. `pilotStart` does not
12
+ * plan, resolve, narrate, or act on anything - it only combines two
13
+ * already-proven, separately-existing primitives into one payload:
14
+ *
15
+ * - `spawnDetachedSession` (same mechanism as `auto --cli --detach`) - a
16
+ * real, persistent, visible-if-headful Playwright session an agent can
17
+ * keep driving across many calls, not a fresh session per request.
18
+ * - `discoverGraph` (same mechanism as `waygraph graph`) - the WHOLE
19
+ * project's Checkpoint/edge/description graph, not just what's reachable
20
+ * from the session's current position, because planning a multi-step
21
+ * route needs to see steps ahead, not just the next one.
22
+ *
23
+ * Driving the session afterward - `auto send <id> "<pick>"`, `auto status
24
+ * <id>`, `auto dom <id>`, `auto trace <id>` - is left entirely to whatever
25
+ * agent is holding it. All four already existed before this file did; see
26
+ * openspec/changes/waygraph-pilot/design.md for the worked example (a real
27
+ * login + add-to-cart + checkout run against examples/saucedemo, driven by
28
+ * hand-picking indices from each status response) that proved this needs no
29
+ * new execution primitive.
30
+ */
31
+ import { discoverGraph } from "./graph.js";
32
+ import { spawnDetachedSession, requestSession } from "./auto-session-ipc.js";
33
+ /**
34
+ * Starts a detached session and reads back its own graph + starting
35
+ * snapshot in one call, so an agent bootstraps with everything it needs
36
+ * (a live session it can keep driving, and the full map of what exists) in
37
+ * one round trip instead of separately knowing to chain `auto --cli
38
+ * --detach` and `waygraph graph` itself.
39
+ */
40
+ export async function pilotStart(init) {
41
+ const [meta, graph] = await Promise.all([
42
+ spawnDetachedSession(init),
43
+ discoverGraph(init.projectDir),
44
+ ]);
45
+ const status = await requestSession(init.projectDir, meta.sessionId, { op: "status" });
46
+ return {
47
+ sessionId: meta.sessionId,
48
+ socketPath: meta.socketPath,
49
+ headless: meta.headless,
50
+ graph,
51
+ snapshot: status.ok ? status.snapshot : null,
52
+ };
53
+ }
package/dist/trait.d.ts CHANGED
@@ -40,6 +40,25 @@ export declare function textEquals(selector: string, expected: string): Trait;
40
40
  * "something matching is visible", not "the selector is unambiguous".
41
41
  */
42
42
  export declare function visible(selector: string): Trait;
43
+ /**
44
+ * `visible`, scoped to a frame (e.g. a mail catcher's message-preview iframe)
45
+ * instead of the top-level page. `page.locator(selector)` alone never reaches
46
+ * inside an iframe - Playwright requires `frameLocator` for that - so
47
+ * `Trait.visible` cannot express this on its own. `frameSelector` finds the
48
+ * iframe itself; `innerSelector` is resolved inside it.
49
+ * @example Trait.frameVisible("#preview-html", 'a[href*="verify"]')
50
+ */
51
+ export declare function frameVisible(frameSelector: string, innerSelector: string): Trait;
52
+ /** `textEquals`, scoped to a frame - see {@link frameVisible} for why this needs its own factory. */
53
+ export declare function frameTextEquals(frameSelector: string, innerSelector: string, expected: string): Trait;
54
+ /**
55
+ * Passes when `innerSelector`'s text inside `frameSelector` contains `expected`
56
+ * as a substring - the common QA shape for "the email says X somewhere" without
57
+ * needing to match the whole body verbatim (surrounding whitespace/markup in a
58
+ * real email template would otherwise break an exact-equality check).
59
+ * @example Trait.frameContains("#preview-html", "body", "Please verify your account")
60
+ */
61
+ export declare function frameContainsText(frameSelector: string, innerSelector: string, expected: string): Trait;
43
62
  /**
44
63
  * Discoverable entry point for the built-in Trait factories - type `Trait.` in
45
64
  * an editor to see `url`/`text`/`visible` offered, instead of needing to
@@ -54,6 +73,9 @@ export declare const Trait: {
54
73
  url: typeof urlMatches;
55
74
  text: typeof textEquals;
56
75
  visible: typeof visible;
76
+ frameVisible: typeof frameVisible;
77
+ frameText: typeof frameTextEquals;
78
+ frameContains: typeof frameContainsText;
57
79
  };
58
80
  /**
59
81
  * Runs every Trait for a Block that just resolved, throwing on the first failure
package/dist/trait.js CHANGED
@@ -22,9 +22,21 @@ export function urlMatches(pattern) {
22
22
  return {
23
23
  name: `url-matches(${JSON.stringify(pattern)})`,
24
24
  async check(page) {
25
- // Bound wait - fail loud instead of hanging forever when navigation never comes
26
- // (e.g. locked_out login still on / while verify demands /inventory.html).
27
- await page.waitForURL(urlPattern, { timeout: 8_000 });
25
+ // No explicit timeout - inherits whatever `page.setDefaultTimeout()`
26
+ // is currently in effect (matching Trait.visible's own precedent),
27
+ // rather than a hardcoded value. Real, confirmed bug this fixes: an
28
+ // explicit `{ timeout: 8_000 }` here silently OVERRODE the much
29
+ // shorter bound `engine.ts`'s own `locate()` sets via
30
+ // `page.setDefaultTimeout()` while probing each NavBlock during
31
+ // `detectHere` (used by resync and every raw click/type/goto/upload) -
32
+ // a project with several `Trait.url` NavBlocks could take 8s PER
33
+ // candidate that doesn't match, turning one resync/raw call into many
34
+ // tens of seconds. Playwright's own ambient default (30s, unless
35
+ // something narrowed it) still fails loud for the normal post-resolve
36
+ // `verify()` case this was originally written for - just via the same
37
+ // mechanism every other Trait already uses instead of its own
38
+ // hardcoded, wrong-context value.
39
+ await page.waitForURL(urlPattern);
28
40
  return true;
29
41
  },
30
42
  };
@@ -53,6 +65,48 @@ export function visible(selector) {
53
65
  },
54
66
  };
55
67
  }
68
+ /**
69
+ * `visible`, scoped to a frame (e.g. a mail catcher's message-preview iframe)
70
+ * instead of the top-level page. `page.locator(selector)` alone never reaches
71
+ * inside an iframe - Playwright requires `frameLocator` for that - so
72
+ * `Trait.visible` cannot express this on its own. `frameSelector` finds the
73
+ * iframe itself; `innerSelector` is resolved inside it.
74
+ * @example Trait.frameVisible("#preview-html", 'a[href*="verify"]')
75
+ */
76
+ export function frameVisible(frameSelector, innerSelector) {
77
+ return {
78
+ name: `frame-visible(${frameSelector}, ${innerSelector})`,
79
+ async check(page) {
80
+ await page.frameLocator(frameSelector).locator(innerSelector).first().waitFor({ state: "visible" });
81
+ return true;
82
+ },
83
+ };
84
+ }
85
+ /** `textEquals`, scoped to a frame - see {@link frameVisible} for why this needs its own factory. */
86
+ export function frameTextEquals(frameSelector, innerSelector, expected) {
87
+ return {
88
+ name: `frame-text-equals(${frameSelector}, ${innerSelector}, ${JSON.stringify(expected)})`,
89
+ async check(page) {
90
+ return (await page.frameLocator(frameSelector).locator(innerSelector).textContent()) === expected;
91
+ },
92
+ };
93
+ }
94
+ /**
95
+ * Passes when `innerSelector`'s text inside `frameSelector` contains `expected`
96
+ * as a substring - the common QA shape for "the email says X somewhere" without
97
+ * needing to match the whole body verbatim (surrounding whitespace/markup in a
98
+ * real email template would otherwise break an exact-equality check).
99
+ * @example Trait.frameContains("#preview-html", "body", "Please verify your account")
100
+ */
101
+ export function frameContainsText(frameSelector, innerSelector, expected) {
102
+ return {
103
+ name: `frame-contains-text(${frameSelector}, ${innerSelector}, ${JSON.stringify(expected)})`,
104
+ async check(page) {
105
+ const text = await page.frameLocator(frameSelector).locator(innerSelector).textContent();
106
+ return (text ?? "").includes(expected);
107
+ },
108
+ };
109
+ }
56
110
  /**
57
111
  * Discoverable entry point for the built-in Trait factories - type `Trait.` in
58
112
  * an editor to see `url`/`text`/`visible` offered, instead of needing to
@@ -67,6 +121,9 @@ export const Trait = {
67
121
  url: urlMatches,
68
122
  text: textEquals,
69
123
  visible,
124
+ frameVisible,
125
+ frameText: frameTextEquals,
126
+ frameContains: frameContainsText,
70
127
  };
71
128
  /**
72
129
  * Runs every Trait for a Block that just resolved, throwing on the first failure
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "waygraph-example-routed-demo",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "Waygraph Map folder convention proof - (group)/<page>/page.block.ts + nav.block.ts + methods/*.block.ts, offline fixture, zero new engine code.",
7
+ "scripts": {
8
+ "typecheck": "tsc --noEmit",
9
+ "test": "npm run fixture:test",
10
+ "fixture": "node scripts/fixture-server.mjs",
11
+ "fixture:test": "node scripts/with-fixture.mjs npx playwright test",
12
+ "graph": "waygraph graph",
13
+ "auto:cli": "node scripts/with-fixture.mjs waygraph auto --cli"
14
+ },
15
+ "waygraph": {
16
+ "baseUrl": "http://127.0.0.1:4277"
17
+ },
18
+ "dependencies": {
19
+ "waygraph": "file:../.."
20
+ },
21
+ "devDependencies": {
22
+ "@playwright/test": "^1.55.0",
23
+ "@types/node": "^26.0.0",
24
+ "typescript": "^5.6.0"
25
+ }
26
+ }
@@ -0,0 +1,20 @@
1
+ import { defineConfig } from "@playwright/test";
2
+
3
+ const PORT = process.env.WAYGRAPH_FIXTURE_PORT || "4277";
4
+ const HOST = process.env.WAYGRAPH_FIXTURE_HOST || "127.0.0.1";
5
+
6
+ export default defineConfig({
7
+ testDir: "./tests",
8
+ fullyParallel: false,
9
+ workers: 1,
10
+ timeout: 30_000,
11
+ reporter: "list",
12
+ use: {
13
+ headless: true,
14
+ baseURL: process.env.WAYGRAPH_BASE_URL || `http://${HOST}:${PORT}`,
15
+ launchOptions: {
16
+ executablePath: process.env.CHROME_PATH || process.env.CHROMIUM_PATH,
17
+ args: ["--no-sandbox", "--disable-dev-shm-usage"],
18
+ },
19
+ },
20
+ });