@e2edev/e2e 0.5.0 → 0.6.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 (415) hide show
  1. package/README.md +64 -25
  2. package/dist/agent/act.d.ts +2 -2
  3. package/dist/agent/act.js +14 -37
  4. package/dist/agent/act.js.map +1 -1
  5. package/dist/agent/actions.d.ts +2 -2
  6. package/dist/agent/actions.d.ts.map +1 -1
  7. package/dist/agent/actions.js +1 -1
  8. package/dist/agent/ai-sdk.d.ts +1 -2
  9. package/dist/agent/ai-sdk.d.ts.map +1 -1
  10. package/dist/agent/ai-sdk.js +1 -2
  11. package/dist/agent/ai-sdk.js.map +1 -1
  12. package/dist/agent/default-agent.d.ts +10 -32
  13. package/dist/agent/default-agent.d.ts.map +1 -1
  14. package/dist/agent/default-agent.js +6 -13
  15. package/dist/agent/default-agent.js.map +1 -1
  16. package/dist/agent/derived.d.ts +1 -1
  17. package/dist/agent/derived.js +1 -1
  18. package/dist/agent/error.d.ts +2 -4
  19. package/dist/agent/error.d.ts.map +1 -1
  20. package/dist/agent/error.js +3 -5
  21. package/dist/agent/error.js.map +1 -1
  22. package/dist/agent/executor.d.ts +23 -52
  23. package/dist/agent/executor.d.ts.map +1 -1
  24. package/dist/agent/executor.js +1 -2
  25. package/dist/agent/executor.js.map +1 -1
  26. package/dist/agent/index.d.ts +1 -1
  27. package/dist/agent/index.d.ts.map +1 -1
  28. package/dist/agent/index.js +10 -7
  29. package/dist/agent/index.js.map +1 -1
  30. package/dist/agent/invocation.d.ts +8 -6
  31. package/dist/agent/invocation.d.ts.map +1 -1
  32. package/dist/agent/invocation.js +9 -4
  33. package/dist/agent/invocation.js.map +1 -1
  34. package/dist/agent/ledger.d.ts +17 -12
  35. package/dist/agent/ledger.d.ts.map +1 -1
  36. package/dist/agent/ledger.js +6 -9
  37. package/dist/agent/ledger.js.map +1 -1
  38. package/dist/agent/model/adapter.d.ts +7 -4
  39. package/dist/agent/model/adapter.d.ts.map +1 -1
  40. package/dist/agent/model/adapter.js +2 -2
  41. package/dist/agent/model/adapter.js.map +1 -1
  42. package/dist/agent/model/router.d.ts +1 -1
  43. package/dist/agent/model/router.d.ts.map +1 -1
  44. package/dist/agent/model/router.js +1 -1
  45. package/dist/agent/model/router.js.map +1 -1
  46. package/dist/agent/model/sdk.d.ts +11 -1
  47. package/dist/agent/model/sdk.d.ts.map +1 -1
  48. package/dist/agent/model/sdk.js +54 -10
  49. package/dist/agent/model/sdk.js.map +1 -1
  50. package/dist/agent/observation.d.ts +7 -7
  51. package/dist/agent/observation.d.ts.map +1 -1
  52. package/dist/agent/observation.js +7 -7
  53. package/dist/agent/observation.js.map +1 -1
  54. package/dist/agent/phases.d.ts +3 -3
  55. package/dist/agent/phases.d.ts.map +1 -1
  56. package/dist/agent/phases.js +4 -4
  57. package/dist/agent/phases.js.map +1 -1
  58. package/dist/agent/primitives.d.ts +4 -25
  59. package/dist/agent/primitives.d.ts.map +1 -1
  60. package/dist/agent/primitives.js +3 -36
  61. package/dist/agent/primitives.js.map +1 -1
  62. package/dist/agent/prompts.d.ts +1 -1
  63. package/dist/agent/prompts.js +1 -1
  64. package/dist/agent/protocol.d.ts +2 -3
  65. package/dist/agent/protocol.d.ts.map +1 -1
  66. package/dist/agent/protocol.js +2 -3
  67. package/dist/agent/protocol.js.map +1 -1
  68. package/dist/agent/public.d.ts +8 -10
  69. package/dist/agent/public.d.ts.map +1 -1
  70. package/dist/agent/public.js +7 -8
  71. package/dist/agent/public.js.map +1 -1
  72. package/dist/agent/replay.d.ts +2 -2
  73. package/dist/agent/replay.d.ts.map +1 -1
  74. package/dist/agent/replay.js +1 -1
  75. package/dist/agent/secrets.d.ts +3 -3
  76. package/dist/agent/secrets.d.ts.map +1 -1
  77. package/dist/agent/secrets.js +4 -4
  78. package/dist/agent/secrets.js.map +1 -1
  79. package/dist/agent/step-cache.d.ts +1 -1
  80. package/dist/agent/step-cache.d.ts.map +1 -1
  81. package/dist/agent/step-cache.js +7 -1
  82. package/dist/agent/step-cache.js.map +1 -1
  83. package/dist/agent/tool-loop.d.ts +9 -33
  84. package/dist/agent/tool-loop.d.ts.map +1 -1
  85. package/dist/agent/tool-loop.js +15 -38
  86. package/dist/agent/tool-loop.js.map +1 -1
  87. package/dist/agent/tool.d.ts +4 -5
  88. package/dist/agent/tool.d.ts.map +1 -1
  89. package/dist/agent/tool.js +2 -7
  90. package/dist/agent/tool.js.map +1 -1
  91. package/dist/cache/anchors.d.ts +2 -2
  92. package/dist/cache/anchors.d.ts.map +1 -1
  93. package/dist/cache/anchors.js +1 -1
  94. package/dist/cache/context.d.ts +6 -1
  95. package/dist/cache/context.d.ts.map +1 -1
  96. package/dist/cache/context.js +2 -1
  97. package/dist/cache/context.js.map +1 -1
  98. package/dist/cache/decide.d.ts +1 -1
  99. package/dist/cache/decide.js +1 -1
  100. package/dist/cache/identity.d.ts +9 -9
  101. package/dist/cache/identity.d.ts.map +1 -1
  102. package/dist/cache/identity.js +6 -6
  103. package/dist/cache/identity.js.map +1 -1
  104. package/dist/cache/recorder.d.ts +4 -2
  105. package/dist/cache/recorder.d.ts.map +1 -1
  106. package/dist/cache/recorder.js +10 -1
  107. package/dist/cache/recorder.js.map +1 -1
  108. package/dist/cache/relocate.d.ts +2 -2
  109. package/dist/cache/relocate.d.ts.map +1 -1
  110. package/dist/cache/relocate.js +1 -1
  111. package/dist/cache/store.d.ts +1 -1
  112. package/dist/cache/store.js +1 -1
  113. package/dist/cache/trace.d.ts +16 -1
  114. package/dist/cache/trace.d.ts.map +1 -1
  115. package/dist/cache/trace.js +22 -1
  116. package/dist/cache/trace.js.map +1 -1
  117. package/dist/cli/bin.js +9 -1
  118. package/dist/cli/bin.js.map +1 -1
  119. package/dist/cli/cache.d.ts +25 -0
  120. package/dist/cli/cache.d.ts.map +1 -0
  121. package/dist/cli/cache.js +249 -0
  122. package/dist/cli/cache.js.map +1 -0
  123. package/dist/cli/docs-url.d.ts +3 -0
  124. package/dist/cli/docs-url.d.ts.map +1 -0
  125. package/dist/cli/docs-url.js +3 -0
  126. package/dist/cli/docs-url.js.map +1 -0
  127. package/dist/cli/guide.d.ts +4 -0
  128. package/dist/cli/guide.d.ts.map +1 -0
  129. package/dist/cli/guide.js +16 -0
  130. package/dist/cli/guide.js.map +1 -0
  131. package/dist/cli/index.d.ts +1 -1
  132. package/dist/cli/index.d.ts.map +1 -1
  133. package/dist/cli/index.js +211 -42
  134. package/dist/cli/index.js.map +1 -1
  135. package/dist/cli/init/agent-skill.d.ts +34 -0
  136. package/dist/cli/init/agent-skill.d.ts.map +1 -0
  137. package/dist/cli/init/agent-skill.js +44 -0
  138. package/dist/cli/init/agent-skill.js.map +1 -0
  139. package/dist/cli/init/{backends.d.ts → engines.d.ts} +39 -32
  140. package/dist/cli/init/engines.d.ts.map +1 -0
  141. package/dist/cli/init/{backends.js → engines.js} +43 -34
  142. package/dist/cli/init/engines.js.map +1 -0
  143. package/dist/cli/init/package.d.ts +19 -3
  144. package/dist/cli/init/package.d.ts.map +1 -1
  145. package/dist/cli/init/package.js +20 -20
  146. package/dist/cli/init/package.js.map +1 -1
  147. package/dist/cli/init/scaffold.d.ts +3 -3
  148. package/dist/cli/init/scaffold.d.ts.map +1 -1
  149. package/dist/cli/init/scaffold.js +13 -12
  150. package/dist/cli/init/scaffold.js.map +1 -1
  151. package/dist/cli/init.d.ts +14 -2
  152. package/dist/cli/init.d.ts.map +1 -1
  153. package/dist/cli/init.js +104 -25
  154. package/dist/cli/init.js.map +1 -1
  155. package/dist/cli/signals.d.ts +5 -2
  156. package/dist/cli/signals.d.ts.map +1 -1
  157. package/dist/cli/signals.js +9 -3
  158. package/dist/cli/signals.js.map +1 -1
  159. package/dist/cli/skill.d.ts +16 -0
  160. package/dist/cli/skill.d.ts.map +1 -0
  161. package/dist/cli/skill.js +57 -0
  162. package/dist/cli/skill.js.map +1 -0
  163. package/dist/collect/collect.d.ts +40 -3
  164. package/dist/collect/collect.d.ts.map +1 -1
  165. package/dist/collect/collect.js +142 -19
  166. package/dist/collect/collect.js.map +1 -1
  167. package/dist/collect/registry.d.ts +1 -1
  168. package/dist/collect/registry.d.ts.map +1 -1
  169. package/dist/collect/registry.js +2 -2
  170. package/dist/collect/registry.js.map +1 -1
  171. package/dist/collect/select.d.ts +1 -1
  172. package/dist/collect/select.d.ts.map +1 -1
  173. package/dist/collect/select.js +81 -8
  174. package/dist/collect/select.js.map +1 -1
  175. package/dist/config/agent.d.ts +14 -2
  176. package/dist/config/agent.d.ts.map +1 -1
  177. package/dist/config/agent.js +36 -11
  178. package/dist/config/agent.js.map +1 -1
  179. package/dist/config/app.d.ts +76 -19
  180. package/dist/config/app.d.ts.map +1 -1
  181. package/dist/config/app.js +159 -104
  182. package/dist/config/app.js.map +1 -1
  183. package/dist/config/diagnose.d.ts +15 -0
  184. package/dist/config/diagnose.d.ts.map +1 -0
  185. package/dist/config/diagnose.js +122 -0
  186. package/dist/config/diagnose.js.map +1 -0
  187. package/dist/config/load.d.ts +9 -1
  188. package/dist/config/load.d.ts.map +1 -1
  189. package/dist/config/load.js +35 -3
  190. package/dist/config/load.js.map +1 -1
  191. package/dist/config/resolve.d.ts +8 -7
  192. package/dist/config/resolve.d.ts.map +1 -1
  193. package/dist/config/resolve.js +73 -40
  194. package/dist/config/resolve.js.map +1 -1
  195. package/dist/config/validate.d.ts +6 -2
  196. package/dist/config/validate.d.ts.map +1 -1
  197. package/dist/config/validate.js +20 -3
  198. package/dist/config/validate.js.map +1 -1
  199. package/dist/credentials.d.ts +1 -1
  200. package/dist/credentials.d.ts.map +1 -1
  201. package/dist/credentials.js +1 -1
  202. package/dist/credentials.js.map +1 -1
  203. package/dist/{backend → engine}/contract.d.ts +27 -19
  204. package/dist/engine/contract.d.ts.map +1 -0
  205. package/dist/{backend → engine}/contract.js +18 -18
  206. package/dist/engine/contract.js.map +1 -0
  207. package/dist/{backend → engine}/index.d.ts +141 -79
  208. package/dist/engine/index.d.ts.map +1 -0
  209. package/dist/{backend → engine}/index.js +62 -37
  210. package/dist/engine/index.js.map +1 -0
  211. package/dist/engine/session.d.ts +23 -0
  212. package/dist/engine/session.d.ts.map +1 -0
  213. package/dist/{backend → engine}/session.js +58 -51
  214. package/dist/engine/session.js.map +1 -0
  215. package/dist/{backend → engine}/surface.d.ts +11 -11
  216. package/dist/engine/surface.d.ts.map +1 -0
  217. package/dist/{backend → engine}/surface.js +2 -2
  218. package/dist/engine/surface.js.map +1 -0
  219. package/dist/engine/timing.d.ts.map +1 -0
  220. package/dist/engine/timing.js +8 -0
  221. package/dist/engine/timing.js.map +1 -0
  222. package/dist/expect/async.d.ts +1 -1
  223. package/dist/expect/async.d.ts.map +1 -1
  224. package/dist/expect/async.js +37 -1
  225. package/dist/expect/async.js.map +1 -1
  226. package/dist/expect/index.js +1 -1
  227. package/dist/expect/values.d.ts +1 -1
  228. package/dist/expect/values.d.ts.map +1 -1
  229. package/dist/expect/values.js +1 -1
  230. package/dist/expect/values.js.map +1 -1
  231. package/dist/index.d.ts +3 -5
  232. package/dist/index.d.ts.map +1 -1
  233. package/dist/index.js +1 -5
  234. package/dist/index.js.map +1 -1
  235. package/dist/internal/brands.d.ts +1 -1
  236. package/dist/internal/brands.d.ts.map +1 -1
  237. package/dist/internal/brands.js +1 -1
  238. package/dist/internal/brands.js.map +1 -1
  239. package/dist/internal/errors.d.ts +37 -19
  240. package/dist/internal/errors.d.ts.map +1 -1
  241. package/dist/internal/errors.js +94 -44
  242. package/dist/internal/errors.js.map +1 -1
  243. package/dist/internal/globs.d.ts +2 -2
  244. package/dist/internal/globs.d.ts.map +1 -1
  245. package/dist/internal/globs.js +2 -2
  246. package/dist/internal/globs.js.map +1 -1
  247. package/dist/internal/ids.d.ts +3 -3
  248. package/dist/internal/ids.d.ts.map +1 -1
  249. package/dist/internal/ids.js +3 -3
  250. package/dist/internal/ids.js.map +1 -1
  251. package/dist/internal/node-version.d.ts +10 -0
  252. package/dist/internal/node-version.d.ts.map +1 -0
  253. package/dist/internal/node-version.js +28 -0
  254. package/dist/internal/node-version.js.map +1 -0
  255. package/dist/internal/objects.d.ts +21 -0
  256. package/dist/internal/objects.d.ts.map +1 -0
  257. package/dist/internal/objects.js +14 -0
  258. package/dist/internal/objects.js.map +1 -0
  259. package/dist/internal/package-manager.d.ts +10 -0
  260. package/dist/internal/package-manager.d.ts.map +1 -0
  261. package/dist/internal/package-manager.js +43 -0
  262. package/dist/internal/package-manager.js.map +1 -0
  263. package/dist/internal/redact.d.ts +1 -1
  264. package/dist/internal/redact.d.ts.map +1 -1
  265. package/dist/internal/redact.js +1 -1
  266. package/dist/internal/redact.js.map +1 -1
  267. package/dist/internal/suggest.d.ts +16 -0
  268. package/dist/internal/suggest.d.ts.map +1 -0
  269. package/dist/internal/suggest.js +61 -0
  270. package/dist/internal/suggest.js.map +1 -0
  271. package/dist/internal/text.d.ts +1 -1
  272. package/dist/internal/text.d.ts.map +1 -1
  273. package/dist/internal/text.js +1 -1
  274. package/dist/internal/text.js.map +1 -1
  275. package/dist/internal/urls.d.ts +3 -3
  276. package/dist/internal/urls.d.ts.map +1 -1
  277. package/dist/internal/urls.js +8 -3
  278. package/dist/internal/urls.js.map +1 -1
  279. package/dist/locator/engine.d.ts +7 -7
  280. package/dist/locator/engine.d.ts.map +1 -1
  281. package/dist/locator/engine.js +14 -14
  282. package/dist/locator/engine.js.map +1 -1
  283. package/dist/locator/expression.d.ts +5 -3
  284. package/dist/locator/expression.d.ts.map +1 -1
  285. package/dist/locator/expression.js +25 -5
  286. package/dist/locator/expression.js.map +1 -1
  287. package/dist/locator/screen.d.ts +1 -1
  288. package/dist/locator/screen.d.ts.map +1 -1
  289. package/dist/locator/screen.js +3 -3
  290. package/dist/locator/screen.js.map +1 -1
  291. package/dist/report/build.d.ts +10 -9
  292. package/dist/report/build.d.ts.map +1 -1
  293. package/dist/report/build.js +25 -22
  294. package/dist/report/build.js.map +1 -1
  295. package/dist/report/code-frame.d.ts +8 -4
  296. package/dist/report/code-frame.d.ts.map +1 -1
  297. package/dist/report/code-frame.js +23 -18
  298. package/dist/report/code-frame.js.map +1 -1
  299. package/dist/report/debug-steps.js +1 -1
  300. package/dist/report/debug-steps.js.map +1 -1
  301. package/dist/report/format.d.ts +73 -0
  302. package/dist/report/format.d.ts.map +1 -0
  303. package/dist/report/format.js +147 -0
  304. package/dist/report/format.js.map +1 -0
  305. package/dist/report/junit.d.ts +1 -1
  306. package/dist/report/junit.d.ts.map +1 -1
  307. package/dist/report/junit.js +1 -1
  308. package/dist/report/junit.js.map +1 -1
  309. package/dist/report/list.d.ts +96 -47
  310. package/dist/report/list.d.ts.map +1 -1
  311. package/dist/report/list.js +489 -244
  312. package/dist/report/list.js.map +1 -1
  313. package/dist/report/live-window.d.ts +28 -0
  314. package/dist/report/live-window.d.ts.map +1 -0
  315. package/dist/report/live-window.js +102 -0
  316. package/dist/report/live-window.js.map +1 -0
  317. package/dist/report/write.d.ts +1 -1
  318. package/dist/report/write.d.ts.map +1 -1
  319. package/dist/report/write.js +1 -1
  320. package/dist/report/write.js.map +1 -1
  321. package/dist/run/budget.d.ts +2 -2
  322. package/dist/run/budget.js +2 -2
  323. package/dist/run/declared-processes.d.ts +30 -0
  324. package/dist/run/declared-processes.d.ts.map +1 -0
  325. package/dist/run/declared-processes.js +61 -0
  326. package/dist/run/declared-processes.js.map +1 -0
  327. package/dist/run/events.d.ts +30 -2
  328. package/dist/run/events.d.ts.map +1 -1
  329. package/dist/run/events.js.map +1 -1
  330. package/dist/run/execute.d.ts +19 -16
  331. package/dist/run/execute.d.ts.map +1 -1
  332. package/dist/run/execute.js +65 -67
  333. package/dist/run/execute.js.map +1 -1
  334. package/dist/run/fixture-recording.d.ts +10 -4
  335. package/dist/run/fixture-recording.d.ts.map +1 -1
  336. package/dist/run/fixture-recording.js +19 -6
  337. package/dist/run/fixture-recording.js.map +1 -1
  338. package/dist/run/fixtures.d.ts +2 -2
  339. package/dist/run/fixtures.d.ts.map +1 -1
  340. package/dist/run/fixtures.js +72 -44
  341. package/dist/run/fixtures.js.map +1 -1
  342. package/dist/run/in-process.d.ts +1 -1
  343. package/dist/run/in-process.js +1 -1
  344. package/dist/run/managed-process.d.ts +46 -9
  345. package/dist/run/managed-process.d.ts.map +1 -1
  346. package/dist/run/managed-process.js +136 -50
  347. package/dist/run/managed-process.js.map +1 -1
  348. package/dist/run/realm.d.ts +2 -2
  349. package/dist/run/realm.js +2 -2
  350. package/dist/run/runner.d.ts +23 -1
  351. package/dist/run/runner.d.ts.map +1 -1
  352. package/dist/run/runner.js +111 -40
  353. package/dist/run/runner.js.map +1 -1
  354. package/dist/run/scheduler.d.ts +5 -5
  355. package/dist/run/scheduler.d.ts.map +1 -1
  356. package/dist/run/scheduler.js +7 -7
  357. package/dist/run/scheduler.js.map +1 -1
  358. package/dist/run/serial.d.ts +10 -2
  359. package/dist/run/serial.d.ts.map +1 -1
  360. package/dist/run/serial.js +5 -3
  361. package/dist/run/serial.js.map +1 -1
  362. package/dist/run/sessions.d.ts +10 -10
  363. package/dist/run/sessions.d.ts.map +1 -1
  364. package/dist/run/sessions.js +11 -11
  365. package/dist/run/sessions.js.map +1 -1
  366. package/dist/run/steps.d.ts +3 -3
  367. package/dist/run/steps.d.ts.map +1 -1
  368. package/dist/run/steps.js +1 -1
  369. package/dist/run/steps.js.map +1 -1
  370. package/dist/run/units.d.ts +1 -1
  371. package/dist/run/units.js +1 -1
  372. package/dist/run/worker/entry.d.ts +1 -1
  373. package/dist/run/worker/entry.js +2 -2
  374. package/dist/run/worker/entry.js.map +1 -1
  375. package/dist/run/worker/protocol.d.ts +18 -6
  376. package/dist/run/worker/protocol.d.ts.map +1 -1
  377. package/dist/run/worker/protocol.js +1 -1
  378. package/dist/run/worker/protocol.js.map +1 -1
  379. package/dist/run/worker/session.d.ts +2 -2
  380. package/dist/run/worker/session.d.ts.map +1 -1
  381. package/dist/run/worker/session.js +5 -3
  382. package/dist/run/worker/session.js.map +1 -1
  383. package/dist/types.d.ts +58 -41
  384. package/dist/types.d.ts.map +1 -1
  385. package/dist/types.js +2 -2
  386. package/package.json +13 -12
  387. package/skills/e2e/SKILL.md +110 -0
  388. package/skills/e2e/references/agent.md +210 -0
  389. package/skills/e2e/references/debugging.md +81 -0
  390. package/skills/e2e/references/running.md +153 -0
  391. package/skills/e2e/references/setup.md +238 -0
  392. package/skills/e2e/references/writing-tests.md +289 -0
  393. package/dist/backend/contract.d.ts.map +0 -1
  394. package/dist/backend/contract.js.map +0 -1
  395. package/dist/backend/index.d.ts.map +0 -1
  396. package/dist/backend/index.js.map +0 -1
  397. package/dist/backend/session.d.ts +0 -23
  398. package/dist/backend/session.d.ts.map +0 -1
  399. package/dist/backend/session.js.map +0 -1
  400. package/dist/backend/surface.d.ts.map +0 -1
  401. package/dist/backend/surface.js.map +0 -1
  402. package/dist/backend/timing.d.ts.map +0 -1
  403. package/dist/backend/timing.js +0 -8
  404. package/dist/backend/timing.js.map +0 -1
  405. package/dist/cli/init/backends.d.ts.map +0 -1
  406. package/dist/cli/init/backends.js.map +0 -1
  407. package/dist/report/live-status.d.ts +0 -51
  408. package/dist/report/live-status.d.ts.map +0 -1
  409. package/dist/report/live-status.js +0 -163
  410. package/dist/report/live-status.js.map +0 -1
  411. package/dist/run/fixture-legacy.d.ts +0 -36
  412. package/dist/run/fixture-legacy.d.ts.map +0 -1
  413. package/dist/run/fixture-legacy.js +0 -143
  414. package/dist/run/fixture-legacy.js.map +0 -1
  415. /package/dist/{backend → engine}/timing.d.ts +0 -0
@@ -0,0 +1,210 @@
1
+ # Agent steps
2
+
3
+ `agent` is a fixture like `screen`. Each call is one bounded invocation: a
4
+ fresh redacted observation, a deadline, a model-call budget, and no shared
5
+ transcript between calls. Deterministic tests never load a model.
6
+
7
+ ## Configure a model
8
+
9
+ There is no default model. Pick one of three shapes:
10
+
11
+ ```ts
12
+ // e2e.config.ts
13
+ import { createAgent } from '@e2edev/e2e/agent';
14
+
15
+ export default {
16
+ // 1. The built-in agent; the model comes from E2E_MODEL at run time.
17
+ agent: createAgent({ system: 'You are a thorough QA agent. Verify every outcome on screen.' }),
18
+
19
+ // 2. An options block: a gateway model string plus project vocabulary.
20
+ // agent: { model: 'anthropic/claude-sonnet-4.5', context: 'A billing dashboard. Plans are Free, Team, and Pro.' },
21
+
22
+ // 3. A live AI SDK model instance for a provider called directly.
23
+ // agent: createAgent({ model: openai('gpt-5.4-mini') }),
24
+ } satisfies E2EConfig;
25
+ ```
26
+
27
+ ```bash
28
+ E2E_MODEL=anthropic/claude-sonnet-4.5 E2E_MODEL_API_KEY=... npx --no-install e2e run
29
+ ```
30
+
31
+ - A `provider/model-id` string is routed through the Vercel AI Gateway; one
32
+ `E2E_MODEL_API_KEY` (or `AI_GATEWAY_API_KEY`) reaches every provider.
33
+ - With no `agent` key at all, the built-in agent still runs and takes its
34
+ model from `E2E_MODEL`. Use `createAgent` for a `system` prompt, tools, or
35
+ a pinned model.
36
+ - `ai@^7` must be installed for any `agent.*` step; the runner loads it
37
+ lazily and fails without it.
38
+ - `context` in the config and `agentContext` on a test or group add trusted
39
+ project vocabulary to every prompt.
40
+ - `visionModel` (or `E2E_VISION_MODEL`) serves the calls that send pixels.
41
+ - Missing model or key: the `agent` fixture fails with `MODEL_UNAVAILABLE`
42
+ (exit 2) at first use.
43
+
44
+ ## act: one goal
45
+
46
+ ```ts
47
+ import { credentials } from '@e2edev/e2e';
48
+
49
+ await agent.act('add a todo named "Buy milk" and mark it done');
50
+ await agent.act('invite {email} as an editor', { email: 'ada@example.test' });
51
+
52
+ const member = credentials.user('member');
53
+ await agent.act('sign in with the given credentials', {
54
+ username: member.username,
55
+ password: member.password, // a Secret: the model sees its name, the runner fills the field
56
+ });
57
+ ```
58
+
59
+ `act(instruction, params?, options?)` plans and performs a multi-action flow
60
+ and ends in a verdict. Passed returns `{ ok: true }`. Failed or blocked
61
+ throws an `AgentError` whose `code` says why: `ACTION_FAILED` for a plain
62
+ failure, `STEP_BUDGET_EXHAUSTED` or `STEP_TIMEOUT` when the budget or the
63
+ clock ran out, and a blocked code (`AUTH_CREDENTIAL_UNAVAILABLE`,
64
+ `ENVIRONMENT_UNAVAILABLE`, `SEED_DATA_MISSING`, `TEST_SETUP_FAILED`,
65
+ `AUTOMATION_UNSUPPORTED`) when something outside the product prevented a
66
+ verdict.
67
+
68
+ Options: `timeout` (default the test timeout), `maxSteps` (default 25
69
+ actions), `maxModelCalls` (default 25). Per-call values can only lower the
70
+ configured limits. `schema` and `vision` on `act` are not implemented and
71
+ reject with `UNSUPPORTED_CAPABILITY`.
72
+
73
+ ## assert, waitFor, extract: one question
74
+
75
+ ```ts
76
+ import { z } from 'zod';
77
+
78
+ await agent.assert('the dashboard shows a trial badge'); // one look, one judgment
79
+
80
+ await agent.waitFor('the export finished and a download link appeared', { // polls
81
+ intervalMs: 500,
82
+ timeout: 120_000,
83
+ });
84
+
85
+ const data = await agent.extract('every todo title and how many remain', { // structured output
86
+ schema: z.object({ titles: z.array(z.string()), remaining: z.number().int() }),
87
+ });
88
+ expect(data.titles).toContain('Buy milk');
89
+ ```
90
+
91
+ - `assert` does not poll. A false judgment is `ASSERTION_FAILED` with the
92
+ model's explanation and a screenshot in the report. Malformed output gets
93
+ one repair round, then `MODEL_OUTPUT_INVALID`.
94
+ - `waitFor` observes every `intervalMs` (default 3 s) and spends a judgment
95
+ only when the screen changed; `STEP_TIMEOUT` after `timeout` (default
96
+ 30 s).
97
+ - `extract` accepts any Standard Schema validator (zod works). Invalid output
98
+ gets one repair round, then `MODEL_OUTPUT_INVALID`.
99
+ - Judgments are never cached and always read a fresh observation.
100
+
101
+ `vision` on a judgment controls the evidence: `false` (default) the semantic
102
+ tree; `true` the tree plus a masked screenshot; `'only'` the screenshot
103
+ alone. Use `'only'` for a question about what the screen presents (an
104
+ overlay, a broken layout, a chart), because the tree would otherwise answer
105
+ first. Pixels show the viewport only and are withheld once a secret was
106
+ filled in the attempt.
107
+
108
+ ## Write instructions the model can execute
109
+
110
+ - One goal per `act`. The order of goals is the test's; the path inside a
111
+ goal is the model's.
112
+ - Use the words on screen: `'open the Billing tab and choose the Pro plan'`,
113
+ not `'upgrade'` when no control says so.
114
+ - Values go in params, never in the sentence:
115
+ `act('rename the project to {name}', { name })`. The runner does not
116
+ expand `{name}`; the model receives the instruction as written plus the
117
+ params as a separate block and reads the value from there.
118
+ - Give vocabulary once, in `agent.context` or `agentContext`, instead of
119
+ repeating it in every instruction.
120
+ - Do not describe mechanics the runner already handles: waiting, scrolling
121
+ into view, retries.
122
+ - Pin the outcome of every `act` deterministically, right after it:
123
+
124
+ ```ts
125
+ await agent.act('create a workspace named "Atlas" on the Pro plan');
126
+ await expect(screen.getByRole('status')).toHaveText('Created "Atlas" on the Pro plan');
127
+ ```
128
+
129
+ The check makes the test model-portable (the path may differ between models,
130
+ the end state may not), and it is what lets the trace cache record the step.
131
+
132
+ ## What the model sees
133
+
134
+ A redacted snapshot of the screen (roles, names, text, states), a summary of
135
+ prior steps, and your context. Never raw HTML, cookies, headers, environment
136
+ values, or a `Secret`'s value; password fields arrive masked. Pixels only
137
+ with `vision`, and only while no secret has been filled. Nothing the model
138
+ returns runs as code or selectors: the runner validates and authorizes every
139
+ tool call before it executes.
140
+
141
+ ## Budgets and cost
142
+
143
+ | Call | Model calls | Default timeout |
144
+ | --- | ---: | --- |
145
+ | `act` | up to `agent.maxModelCalls` (25) | the test `timeout`, 120 s |
146
+ | `assert` | 2 | 30 s |
147
+ | `extract` | 2 | 30 s |
148
+ | `waitFor` | up to `agent.maxModelCalls` (25) | 30 s |
149
+
150
+ - Keep `agent.*` for steps whose path or wording varies; `expect` and
151
+ `screen` are free.
152
+ - Slow providers: raise the test `timeout` and `actionTimeout` (each
153
+ observation and action inside a step is bounded by it) rather than reading
154
+ latency as a defect. `STEP_TIMEOUT` and `STEP_BUDGET_EXHAUSTED` are test
155
+ failures: scope the goal smaller or split it.
156
+ - `--debug` prints a per-step table (duration, model calls, tokens, cost)
157
+ after the run and saves each step's transcript as an artifact.
158
+
159
+ ## The trace cache
160
+
161
+ Each passing `agent.act` records the actions it performed. The next run
162
+ replays them with zero model calls and hands back to the live agent the
163
+ moment the app no longer matches the recording, or when the recorded end
164
+ state is not on screen after the replay.
165
+
166
+ - On by default (`read-write`), `read-only` in CI, `cache: 'off'` in the
167
+ config or `--no-cache` on a run to disable. Entries live in `.e2e/cache/`;
168
+ deleting the directory only slows the next run.
169
+ - An entry is written only after a later verification step passes
170
+ (`expect`, `locator.waitFor`, `agent.assert`, `agent.waitFor`). An `act`
171
+ nothing checks is never replayed.
172
+ - `e2e init` gitignores `.e2e/cache/`; committing entries is opt-in. Remove
173
+ that line to share replays with CI and teammates (CI stays `read-only`
174
+ unless `cache: 'read-write'` is set explicitly).
175
+ - A failing run evicts the entries it implicates. To rule the cache out of a
176
+ failure, run with `--no-cache`.
177
+
178
+ ## Inspect what the model did
179
+
180
+ ```bash
181
+ npx --no-install e2e run tests/checkout.e2e.ts --debug # step table, transcripts as artifacts
182
+ npx --no-install e2e run tests/checkout.e2e.ts --ai-trace # writes .e2e/ai-trace.json
183
+ npx unbox-ai runs .e2e/ai-trace.json # one line per agent step
184
+ npx unbox-ai summary .e2e/ai-trace.json --run 0 # turns, tokens, tool calls of one step
185
+ ```
186
+
187
+ Never read `.e2e/ai-trace.json` directly; it is megabytes of resent context.
188
+ Use `--no-cache` when the whole flow should be traced, since replayed steps
189
+ make no model calls.
190
+
191
+ ## Beyond the built-in agent
192
+
193
+ - `createAgent({ tools: { seedCart } })` adds AI SDK tools wrapped with
194
+ `defineTool(tool({ ... }), { mutates: true })` from `@e2edev/e2e/agent`, so
195
+ a flow can call a test API mid-step.
196
+ - `createToolLoopExecutor` keeps the loop and replaces the prompt and the
197
+ tool vocabulary.
198
+ - Any object implementing `StepExecutor` (`{ name, version, cache, runStep(ctx) }`)
199
+ can be the `agent`; the runner still owns observations, actions, budgets,
200
+ and the report.
201
+
202
+ Full reference: https://e2e.docs.buildwithfern.com/agents
203
+
204
+ ## In CI
205
+
206
+ Deterministic tests gate merges; agentic tests are opt-in. Keep them in a
207
+ separate config (`e2e.agent.config.ts` with its own `tests` glob and a larger
208
+ `timeout`), run them on a schedule or `workflow_dispatch`, and pass the model
209
+ through `env: { E2E_MODEL, E2E_MODEL_API_KEY }` from CI variables and
210
+ secrets.
@@ -0,0 +1,81 @@
1
+ # Debugging a failing run
2
+
3
+ ## Read the failure
4
+
5
+ 1. The `list` reporter ends with a `Failed Tests` section: each failure shows
6
+ the error code, the message, the test's failing line, and a code frame.
7
+ The summary's `Report` line names `.e2e/report.json`.
8
+ 2. `.e2e/report.json` has everything:
9
+
10
+ ```bash
11
+ jq '.run | {status, exitCode, errors}' .e2e/report.json # run-level failures
12
+ jq '.run.results[] | select(.status != "passed") | {titlePath, file, status}' .e2e/report.json
13
+ jq '.run.results[] | select(.status != "passed") | .attempts[-1]
14
+ | {status, error, steps: [.steps[] | select(.status != "passed") | {api, label, status, error}], artifacts}' .e2e/report.json
15
+ ```
16
+
17
+ 3. Artifacts named there live under `.e2e/artifacts/`: screenshots, a
18
+ Playwright `trace.zip` per attempt (`npx playwright show-trace <file>`),
19
+ downloads, and with `--debug` the transcript of every agent step.
20
+
21
+ ## Error codes and what to do
22
+
23
+ | Code | Usual cause | Fix |
24
+ | --- | --- | --- |
25
+ | `CONFIG_LOAD_FAILED` mentioning ES modules | `package.json` lacks `"type": "module"` | `npm pkg set type=module`, or rename the config to `.mts` and keep the tests in an ESM package |
26
+ | `INVALID_CONFIG` | Unknown key or a stale shape: a top-level `app`, `defineConfig`, a `backend` key, `json` combined with `list` reporters | Move app options into `playwright({ ... })`; use `satisfies E2EConfig`; the message names the key |
27
+ | `CONFIG_NOT_FOUND`, `CONFIG_AMBIGUOUS` | Wrong `--config` path; both `.ts` and `.mts` present | Fix the path; keep one config file |
28
+ | `NO_TESTS` | The glob or a positional matched nothing | The message names each positional that matched nothing. Check `tests` in the config and the `.e2e.ts` suffix |
29
+ | `COLLECTION_ERROR` | `async` describe body, `test.setup` inside `describe`, an option forbidden in a serial group, registration outside collection | Rework the structure per `writing-tests` |
30
+ | `APP_UNREACHABLE` | `command` never answered `readyUrl` within `startupTimeout`; a service exited non-zero | Set `command.log` and read it; check the port and `url`; pass the env the app needs through `command.env`; raise `startupTimeout` |
31
+ | `APP_ALREADY_RUNNING` | Something already serves `url` when the runner wanted to start `command` | Stop it, or set `reuseExisting: true` for local runs |
32
+ | `APP_URL_REQUIRED` | A navigation on an engine without `url` | Add `url` to `playwright({ ... })` |
33
+ | `LOCATOR_NOT_FOUND` | Wrong role or name, text not exact, element off screen or inside an iframe, page not open | Read the markup for the accessible name; try `exact: false` or a RegExp; `web.frameLocator` for iframes; `app.open()` first; `--headed` to look |
34
+ | `LOCATOR_AMBIGUOUS` | Two matches: a hidden duplicate, a repeated label | Add `{ name }`, scope under a container, `filter`, `first()`, or `{ visible: true }` |
35
+ | `ASSERTION_FAILED` | The expectation is wrong, or the state settles later than 5 s; for `agent.assert`, the judgment was false (explanation in the report) | Compare with the actual text in the report or screenshot; `{ timeout }` on the matcher; rewrite the question |
36
+ | `ACTION_FAILED` | Element not actionable (covered, disabled, detached) or an operation timed out | Wait on the right condition with `expect` first; close overlays; check `actionTimeout` |
37
+ | `TEST_TIMEOUT` | The attempt exceeded `timeout` (120 s) | Split the test, or raise `timeout` for slow flows and agent steps |
38
+ | `MODEL_UNAVAILABLE` | No `E2E_MODEL`, no key, or an unknown provider | Export `E2E_MODEL=provider/model-id` and `E2E_MODEL_API_KEY`; confirm the model id |
39
+ | `MODEL_PROVIDER_FAILED` | Network, 5xx, rate limit, or no credits after the transport retries | Check the key and the quota; retry; exit code 3 |
40
+ | `STEP_TIMEOUT`, `STEP_BUDGET_EXHAUSTED` | The goal was too big or ambiguous, or the provider slow | Split the goal, use on-screen wording, add `context`, raise `timeout` and `actionTimeout`, `--debug` to read the transcript |
41
+ | `POLICY_DENIED` | Navigation outside `allowedOrigins`; a `Secret` given to a sink that is not a password field; reading a secure field | Add the origin to `allowedOrigins`; fill secrets only into password inputs; assert the outcome instead of the value |
42
+ | `UNSUPPORTED_CAPABILITY` | A fixture the engine does not contribute (`web` on a device), `schema` or `vision` on `act`, an action the surface lacks | Declare `requires: ['web']`; drop the option; use a supported action |
43
+ | `SESSION_UNAVAILABLE`, `SESSION_CONTRACT` | `session: 'x'` with no setup saving `x`; a setup that did not save every declared name | Add or fix the `test.setup` |
44
+ | `ONLY_IN_CI` | `test.only` reached CI | Remove it |
45
+ | `BROWSER_INSTALL_FAILED`, `LAUNCH_TIMEOUT` | Browser download or launch failed | `npx playwright install chromium --with-deps`; raise `launchTimeout` on slow machines |
46
+ | `AUTH_CREDENTIAL_UNAVAILABLE` | `credentials.user('x')` for an undeclared name | Add it to `config.credentials` |
47
+
48
+ ## Tools
49
+
50
+ | Do | When |
51
+ | --- | --- |
52
+ | `--headed` | Watch the flow; confirm what the page shows at the failing step |
53
+ | `--workers 1 --retries 0` | Remove parallelism and retries from the picture |
54
+ | `--no-cache` | Rule out a stale `agent.act` replay |
55
+ | `--debug` | Read each agent step's duration, model calls, cost, and transcript |
56
+ | `--ai-trace`, then `npx unbox-ai runs .e2e/ai-trace.json` | See exactly what the model was shown and called |
57
+ | `command.log: '.e2e/logs/app.log'` | Read the app's own output when it never becomes ready or errors mid-test |
58
+ | `await app.screenshot('before-submit')` | Attach evidence at a chosen point |
59
+ | `CI=1 npx --no-install e2e run` | Reproduce CI-only behaviour: `ONLY_IN_CI`, read-only cache, `reuseExisting` ignored |
60
+
61
+ ## Flaky tests
62
+
63
+ - A read (`textContent()`, `count()`) captured a value mid-update: replace
64
+ it with a matcher.
65
+ - Shared data between tests: unique names per run, cleanup in `afterEach`,
66
+ or a `serial` group.
67
+ - The app is not ready: assert on the element you are about to use rather
68
+ than on the previous page.
69
+ - An agent judgment asserts an exact phrasing: judge the fact, and add a
70
+ deterministic `expect` beside it.
71
+ - Timing under load: `retries` masks the cause; `--workers 1` and
72
+ `--headed` usually show it.
73
+
74
+ ## Is it the app?
75
+
76
+ A deterministic step that fails every run at the same place with the same
77
+ code is a product bug or a changed screen, not flakiness. Reproduce once
78
+ with `--headed`, then fix the app, or update the locator and the expectation
79
+ together. A blocked agent step (`AUTH_CREDENTIAL_UNAVAILABLE`,
80
+ `ENVIRONMENT_UNAVAILABLE`, `SEED_DATA_MISSING`) exits 2 or 3 on purpose: fix
81
+ the environment, not the test.
@@ -0,0 +1,153 @@
1
+ # Running tests
2
+
3
+ ## Commands
4
+
5
+ ```bash
6
+ npx --no-install e2e run [files...] [options] # run tests
7
+ npx --no-install e2e list [files...] [options] # print what run would select, without running
8
+ npx --no-install e2e init [--yes] # scaffold a project, refresh the agent skill
9
+ npx --no-install e2e guide [topic] # print this skill: setup, writing-tests, agent, running, debugging
10
+ npx --no-install e2e cache ls|clear|stats # read or empty the trace cache
11
+ ```
12
+
13
+ `run` flags:
14
+
15
+ | Flag | Effect |
16
+ | --- | --- |
17
+ | `[files...]` | Files, directories, or quoted globs relative to the project root. They narrow the config `tests` glob, never bypass it. |
18
+ | `--config <path>` | Explicit config file. Default: `e2e.config.ts` or `.mts` found upward from the working directory. |
19
+ | `--target <ids>` | Comma-separated target names. |
20
+ | `--tag <tag>` | Repeatable tag filter; `--tag-mode all` requires every tag. |
21
+ | `--headed` | Visible browser or simulator when the engine supports it. |
22
+ | `--workers <n>`, `--retries <n>` | Override the resolved values. |
23
+ | `--reporter <ids>` | `list`, `json`, `junit`, comma-separated. `json` cannot combine with `list`. |
24
+ | `--artifacts <dir>` | Artifact root, default `.e2e/artifacts`. |
25
+ | `--no-cache` | Run with the trace cache off. |
26
+ | `--pass-with-no-tests` | Exit 0 when nothing matches instead of `NO_TESTS`. |
27
+ | `--debug` | Phase timings and an agent step table on stderr; step transcripts saved as artifacts. |
28
+ | `--ai-trace` | Record every model call to `.e2e/ai-trace.json`. |
29
+
30
+ ```bash
31
+ npx --no-install e2e run tests/signup.e2e.ts
32
+ npx --no-install e2e run tests/agent --tag smoke
33
+ npx --no-install e2e run 'tests/**/*.smoke.e2e.ts' --target chromium --workers 1 --retries 0
34
+ CI=1 npx --no-install e2e run # reproduce the CI defaults locally
35
+ ```
36
+
37
+ `list` takes the same files and the selection flags (`--config`, `--target`,
38
+ `--tag`, `--tag-mode`, `--pass-with-no-tests`) and prints one line per
39
+ test-target pair, `file › title [target]`, then exits without starting the
40
+ app, an engine, or a worker. `--reporter json` prints `{ "pairs": [...] }`.
41
+ Use it to check a filter before a run.
42
+
43
+ ```bash
44
+ npx --no-install e2e list --tag smoke
45
+ npx --no-install e2e list tests/signup.e2e.ts --reporter json
46
+ ```
47
+
48
+ A `package.json` script keeps it short: `"test:e2e": "e2e run"`, then
49
+ `pnpm test:e2e tests/signup.e2e.ts`.
50
+
51
+ ## The trace cache
52
+
53
+ Entries live under `.e2e/cache/`, one file per key, named after the key
54
+ digest. `cache` commands read the same config as `run`, so `--config` and
55
+ `cache.dir` point them at the right store.
56
+
57
+ | Command | Prints |
58
+ | --- | --- |
59
+ | `e2e cache ls` | One row per entry: test, target, instruction digest, age, action count. |
60
+ | `e2e cache stats` | Directory, entry count, total size. |
61
+ | `e2e cache clear` | Deletes the entries and the directory; files the runner never wrote stay. |
62
+
63
+ Use `ls` to see what a committed cache would replay, and `clear` when a
64
+ recorded flow is stale — `--no-cache` only skips the cache for one run.
65
+
66
+ ## Output
67
+
68
+ - `list` (default): one line per file and target, a `Failed Tests` section
69
+ with each error, its code, the failing line and a code frame, then a
70
+ summary (`Test Files`, `Tests`, `AI`, `Duration`, `Report`).
71
+ - `.e2e/report.json` is written on every run whatever the reporters:
72
+ `run.status`, `run.exitCode`, `run.errors[]` (run-level failures such as
73
+ `APP_UNREACHABLE`), and `run.results[]`, one per test and target, with
74
+ `titlePath`, `file`, `source`, `status`, and `attempts[]` holding `steps[]`,
75
+ `artifacts[]`, and `error`.
76
+ - `junit`: `.e2e/junit.xml` beside the report, for CI test summaries.
77
+ Combine it with the terminal output: `--reporter list,junit`.
78
+ - `json`: the report document on stdout.
79
+ - Artifacts (screenshots, Playwright traces, `--debug` transcripts,
80
+ downloads) live under `.e2e/artifacts/`; every path is recorded in the
81
+ report.
82
+
83
+ ## Exit codes
84
+
85
+ | Code | Meaning |
86
+ | ---: | --- |
87
+ | 0 | Every selected test passed, was flaky, or was skipped |
88
+ | 1 | A test or setup test failed or timed out |
89
+ | 2 | CLI, config, collection, credential, model-config, or policy error |
90
+ | 3 | Engine, app process, model provider, artifact, or cleanup failure |
91
+ | 4 | Internal runner error |
92
+ | 130 | Interrupted |
93
+
94
+ The highest code present wins for a mixed run (`130 > 4 > 3 > 2 > 1 > 0`).
95
+ Do not retry a job on exit 2: it is deterministic. Exit 3 is the only one
96
+ where a job-level retry makes sense.
97
+
98
+ Ctrl-C once interrupts and still writes the report; twice forces teardown;
99
+ three times exits at once.
100
+
101
+ ## Continuous integration
102
+
103
+ CI mode is on when `CI` is set (and not `0` or `false`). It changes
104
+ `retries` to 1, `workers` to 1, rejects `test.only` with `ONLY_IN_CI`, makes
105
+ the trace cache `read-only`, and ignores `reuseExisting`.
106
+
107
+ ```yaml
108
+ # .github/workflows/e2e.yml
109
+ name: e2e
110
+ on:
111
+ pull_request:
112
+ push:
113
+ branches: [main]
114
+ permissions:
115
+ contents: read
116
+ jobs:
117
+ e2e:
118
+ runs-on: ubuntu-latest
119
+ steps:
120
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
121
+ - uses: pnpm/action-setup@9fd676a19091d4595eefd76e4bd31c97133911f1 # v4.2.0
122
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
123
+ with:
124
+ node-version: 26
125
+ cache: pnpm
126
+ - run: pnpm install --frozen-lockfile
127
+ - run: npx playwright install chromium --with-deps
128
+ - run: npx --no-install e2e run --reporter list,junit
129
+ env:
130
+ E2E_USER_ADMIN_USERNAME: ${{ secrets.E2E_USER_ADMIN_USERNAME }}
131
+ E2E_USER_ADMIN_PASSWORD: ${{ secrets.E2E_USER_ADMIN_PASSWORD }}
132
+ - if: ${{ !cancelled() }}
133
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
134
+ with:
135
+ name: e2e-report
136
+ path: |
137
+ .e2e/report.json
138
+ .e2e/junit.xml
139
+ - if: failure()
140
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
141
+ with:
142
+ name: e2e-artifacts
143
+ path: .e2e/artifacts
144
+ retention-days: 7
145
+ ```
146
+
147
+ - Install browsers as their own step so the download never counts against a
148
+ launch timeout.
149
+ - Start the app through the engine's `command`; the runner tears it down on
150
+ every exit path.
151
+ - Agentic suites: a separate config, run on `schedule` or
152
+ `workflow_dispatch`, `E2E_MODEL` as a CI variable and `E2E_MODEL_API_KEY`
153
+ as a secret, never a required check.