@reticlehq/server 3.8.2 → 3.10.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 (477) hide show
  1. package/LICENSE +201 -104
  2. package/NOTICE +5 -0
  3. package/README.md +1 -1
  4. package/SKILL.md +13 -12
  5. package/bin/launch.js +13 -0
  6. package/bin/package.json +3 -0
  7. package/bin/reticle.js +66 -8
  8. package/dist/chromium-launch-options.d.ts +25 -0
  9. package/dist/chromium-launch-options.js +21 -1
  10. package/dist/command/cli/adhoc-suite.js +2 -0
  11. package/dist/command/cli/adhoc-verdict.d.ts +2 -43
  12. package/dist/command/cli/adhoc-verdict.js +50 -160
  13. package/dist/command/cli/answers/gate-exit.d.ts +6 -2
  14. package/dist/command/cli/answers/gate-exit.js +12 -2
  15. package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
  16. package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
  17. package/dist/command/cli/ci-hint.d.ts +44 -0
  18. package/dist/command/cli/ci-hint.js +72 -0
  19. package/dist/command/cli/cli-doctor.d.ts +6 -1
  20. package/dist/command/cli/cli-doctor.js +46 -5
  21. package/dist/command/cli/cli-flow-commands.d.ts +7 -1
  22. package/dist/command/cli/cli-flow-commands.js +39 -24
  23. package/dist/command/cli/cli-kill.d.ts +21 -0
  24. package/dist/command/cli/cli-kill.js +47 -4
  25. package/dist/command/cli/cli-parse-compare.d.ts +22 -0
  26. package/dist/command/cli/cli-parse-compare.js +75 -0
  27. package/dist/command/cli/cli-parse-grade.d.ts +36 -0
  28. package/dist/command/cli/cli-parse-grade.js +75 -0
  29. package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
  30. package/dist/command/cli/cli-parse-grammar.js +18 -20
  31. package/dist/command/cli/cli-parse-verify.d.ts +10 -0
  32. package/dist/command/cli/cli-parse-verify.js +63 -14
  33. package/dist/command/cli/cli-parse.d.ts +22 -17
  34. package/dist/command/cli/cli-parse.js +54 -32
  35. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  36. package/dist/command/cli/cli-update-commands.js +29 -20
  37. package/dist/command/cli/cli-usage.js +61 -31
  38. package/dist/command/cli/cli-verify.d.ts +17 -3
  39. package/dist/command/cli/cli-verify.js +59 -21
  40. package/dist/command/cli/cloud-cli.js +2 -119
  41. package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
  42. package/dist/command/cli/daemon-link/daemon-client.js +205 -0
  43. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +6 -1
  44. package/dist/command/cli/doctor/browser/chromium-hint.js +11 -5
  45. package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
  46. package/dist/command/cli/doctor/doctor-fix.d.ts +44 -0
  47. package/dist/command/cli/doctor/doctor-fix.js +89 -0
  48. package/dist/command/cli/doctor/doctor-launcher-line.d.ts +23 -0
  49. package/dist/command/cli/doctor/doctor-launcher-line.js +60 -0
  50. package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
  51. package/dist/command/cli/doctor/doctor-rows.js +4 -0
  52. package/dist/command/cli/doctor-repairs.d.ts +64 -0
  53. package/dist/command/cli/doctor-repairs.js +214 -0
  54. package/dist/command/cli/drive/drive-command.js +3 -7
  55. package/dist/command/cli/ensure-daemon.d.ts +26 -0
  56. package/dist/command/cli/ensure-daemon.js +57 -0
  57. package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
  58. package/dist/command/cli/finished-run/grade-command.js +180 -0
  59. package/dist/command/cli/first-use-checks.d.ts +41 -0
  60. package/dist/command/cli/first-use-checks.js +127 -0
  61. package/dist/command/cli/first-verdict-node.d.ts +3 -0
  62. package/dist/command/cli/first-verdict-node.js +189 -0
  63. package/dist/command/cli/first-verdict.d.ts +126 -0
  64. package/dist/command/cli/first-verdict.js +267 -0
  65. package/dist/command/cli/launch/sdk-version-match.d.ts +3 -2
  66. package/dist/command/cli/launch/sdk-version-match.js +3 -2
  67. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
  68. package/dist/command/cli/lifecycle/daemon-lifecycle.js +7 -6
  69. package/dist/command/cli/mcp-command.js +13 -4
  70. package/dist/command/cli/ports/port-holder.d.ts +11 -0
  71. package/dist/command/cli/ports/port-holder.js +36 -4
  72. package/dist/command/cli/setup-mcp-cli.d.ts +27 -1
  73. package/dist/command/cli/setup-mcp-cli.js +98 -23
  74. package/dist/command/cli/status/status-lines.d.ts +2 -0
  75. package/dist/command/cli/status/status-lines.js +72 -17
  76. package/dist/command/cli/status-fields.d.ts +5 -1
  77. package/dist/command/cli/status-fields.js +24 -6
  78. package/dist/command/cli/try-command.d.ts +7 -0
  79. package/dist/command/cli/try-command.js +11 -3
  80. package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
  81. package/dist/command/cli/two-builds/compare-command.js +314 -0
  82. package/dist/command/cli/verify-against.d.ts +61 -0
  83. package/dist/command/cli/verify-against.js +187 -0
  84. package/dist/command/cli/verify-no-session.d.ts +2 -0
  85. package/dist/command/cli/verify-no-session.js +13 -0
  86. package/dist/command/cli/verify-results.d.ts +35 -2
  87. package/dist/command/cli/verify-results.js +76 -10
  88. package/dist/command/cli.js +112 -70
  89. package/dist/command/daemon/binding/daemon-status-probe.d.ts +3 -1
  90. package/dist/command/daemon/binding/daemon-status-probe.js +4 -2
  91. package/dist/command/daemon/daemon-argv.d.ts +9 -0
  92. package/dist/command/daemon/daemon-argv.js +11 -0
  93. package/dist/command/daemon/daemon-resolve.d.ts +8 -1
  94. package/dist/command/daemon/daemon-resolve.js +27 -1
  95. package/dist/command/daemon/daemon.d.ts +22 -4
  96. package/dist/command/daemon/daemon.js +52 -10
  97. package/dist/command/daemon/lifetime/daemon-exit.d.ts +24 -0
  98. package/dist/command/daemon/lifetime/daemon-exit.js +39 -0
  99. package/dist/command/daemon/lifetime/death-cause.d.ts +15 -0
  100. package/dist/command/daemon/lifetime/death-cause.js +149 -0
  101. package/dist/command/daemon/lifetime/heartbeat.d.ts +7 -15
  102. package/dist/command/daemon/lifetime/heartbeat.js +11 -15
  103. package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
  104. package/dist/command/daemon/lifetime/startup-failure.js +62 -0
  105. package/dist/command/daemon/port-state.d.ts +14 -0
  106. package/dist/command/daemon/port-state.js +31 -0
  107. package/dist/command/setup/agent-configs.d.ts +2 -0
  108. package/dist/command/setup/agent-configs.js +42 -15
  109. package/dist/command/setup/agent-next-steps.d.ts +77 -0
  110. package/dist/command/setup/agent-next-steps.js +284 -0
  111. package/dist/command/setup/agent-writer.js +11 -6
  112. package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
  113. package/dist/command/setup/bringup/dev-server-files.js +17 -0
  114. package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
  115. package/dist/command/setup/bringup/static-page-server.js +43 -9
  116. package/dist/command/setup/init/init-host.js +2 -0
  117. package/dist/command/setup/init/init-runtime.d.ts +6 -1
  118. package/dist/command/setup/init/init-runtime.js +63 -21
  119. package/dist/command/setup/mcp-launcher.d.ts +3 -0
  120. package/dist/command/setup/mcp-launcher.js +11 -0
  121. package/dist/command/setup/node-effects.d.ts +3 -2
  122. package/dist/command/setup/node-effects.js +9 -3
  123. package/dist/command/setup/remaining-steps.d.ts +8 -0
  124. package/dist/command/setup/remaining-steps.js +10 -0
  125. package/dist/command/setup/run-setup.d.ts +2 -0
  126. package/dist/command/setup/run-setup.js +9 -1
  127. package/dist/command/setup/setup-command.js +36 -15
  128. package/dist/command/setup/setup-mcp.d.ts +8 -0
  129. package/dist/command/setup/setup-mcp.js +47 -20
  130. package/dist/command/setup/start-dev-server.d.ts +8 -0
  131. package/dist/command/setup/start-dev-server.js +43 -0
  132. package/dist/command/update/updater.js +6 -0
  133. package/dist/command/version/identity/server-version.d.ts +2 -0
  134. package/dist/command/version/identity/server-version.js +2 -0
  135. package/dist/command/version/version-skew.d.ts +7 -0
  136. package/dist/command/version/version-skew.js +14 -3
  137. package/dist/features/compare/action-plan.d.ts +260 -0
  138. package/dist/features/compare/action-plan.js +543 -0
  139. package/dist/features/compare/behaviour-compare.d.ts +24 -0
  140. package/dist/features/compare/behaviour-compare.js +85 -0
  141. package/dist/features/compare/behaviour-diff.d.ts +97 -0
  142. package/dist/features/compare/behaviour-diff.js +202 -0
  143. package/dist/features/compare/behaviour-report.d.ts +40 -0
  144. package/dist/features/compare/behaviour-report.js +128 -0
  145. package/dist/features/compare/build-readings.d.ts +106 -0
  146. package/dist/features/compare/build-readings.js +250 -0
  147. package/dist/features/compare/compare-diff.d.ts +62 -0
  148. package/dist/features/compare/compare-diff.js +177 -0
  149. package/dist/features/compare/compare-report-html.d.ts +6 -0
  150. package/dist/features/compare/compare-report-html.js +102 -0
  151. package/dist/features/compare/compare-report.d.ts +15 -0
  152. package/dist/features/compare/compare-report.js +73 -0
  153. package/dist/features/compare/compare-run.d.ts +60 -0
  154. package/dist/features/compare/compare-run.js +123 -0
  155. package/dist/features/compare/outside/effect-lines.d.ts +26 -0
  156. package/dist/features/compare/outside/effect-lines.js +103 -0
  157. package/dist/features/compare/outside/state-sources.d.ts +76 -0
  158. package/dist/features/compare/outside/state-sources.js +164 -0
  159. package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
  160. package/dist/features/compare/outside/value-dictionary.js +227 -0
  161. package/dist/features/compare/visible-content.d.ts +20 -0
  162. package/dist/features/compare/visible-content.js +125 -0
  163. package/dist/features/crawl/crawl-empty.js +2 -1
  164. package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
  165. package/dist/features/crawl/crawl-exhaustive.js +138 -0
  166. package/dist/features/crawl/crawl-tools.d.ts +9 -2
  167. package/dist/features/crawl/crawl-tools.js +28 -12
  168. package/dist/features/crawl/crawl.d.ts +8 -9
  169. package/dist/features/crawl/crawl.js +61 -65
  170. package/dist/features/exhaust/app-coverage.d.ts +5 -1
  171. package/dist/features/exhaust/app-coverage.js +3 -1
  172. package/dist/features/exhaust/code-coverage.js +8 -4
  173. package/dist/features/exhaust/edge-cases.d.ts +133 -0
  174. package/dist/features/exhaust/edge-cases.js +310 -0
  175. package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
  176. package/dist/features/exhaust/edges/edge-drive.js +364 -0
  177. package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
  178. package/dist/features/exhaust/edges/edge-run.js +169 -0
  179. package/dist/features/exhaust/explorer.d.ts +87 -14
  180. package/dist/features/exhaust/explorer.js +159 -25
  181. package/dist/features/exhaust/failure-branches.d.ts +5 -0
  182. package/dist/features/exhaust/failure-branches.js +29 -11
  183. package/dist/features/exhaust/invariants.d.ts +112 -0
  184. package/dist/features/exhaust/invariants.js +188 -0
  185. package/dist/features/exhaust/ledger.d.ts +68 -0
  186. package/dist/features/exhaust/ledger.js +121 -13
  187. package/dist/features/exhaust/session-fold.d.ts +10 -0
  188. package/dist/features/exhaust/session-fold.js +46 -3
  189. package/dist/features/exhaust/session-port.d.ts +3 -1
  190. package/dist/features/exhaust/session-port.js +97 -28
  191. package/dist/features/grade/grade-answer.d.ts +99 -0
  192. package/dist/features/grade/grade-answer.js +112 -0
  193. package/dist/features/grade/grade-noise.d.ts +27 -0
  194. package/dist/features/grade/grade-noise.js +55 -0
  195. package/dist/features/grade/grade-renumber.d.ts +12 -0
  196. package/dist/features/grade/grade-renumber.js +86 -0
  197. package/dist/features/grade/grade-state.d.ts +50 -0
  198. package/dist/features/grade/grade-state.js +93 -0
  199. package/dist/features/grade/grade-suggest.d.ts +33 -0
  200. package/dist/features/grade/grade-suggest.js +98 -0
  201. package/dist/features/license/license.d.ts +11 -71
  202. package/dist/features/license/license.js +43 -136
  203. package/dist/features/visual/desktop-capture.d.ts +33 -0
  204. package/dist/features/visual/desktop-capture.js +94 -0
  205. package/dist/features/visual/step-shots.js +139 -35
  206. package/dist/features/visual/visual-tools.d.ts +0 -18
  207. package/dist/features/visual/visual-tools.js +67 -99
  208. package/dist/import-graph.js +5 -6
  209. package/dist/index.d.ts +3 -1
  210. package/dist/index.js +19 -7
  211. package/dist/judgement/capsule/causal-summary.js +9 -2
  212. package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
  213. package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
  214. package/dist/judgement/runs/reticle-runner.js +16 -2
  215. package/dist/judgement/runs/run-ledger.d.ts +7 -5
  216. package/dist/judgement/runs/run-ledger.js +98 -69
  217. package/dist/judgement/runs/runner-port.d.ts +2 -0
  218. package/dist/judgement/runs/runner-port.js +23 -5
  219. package/dist/judgement/runs/verification-sync.d.ts +6 -3
  220. package/dist/judgement/runs/verification-sync.js +21 -10
  221. package/dist/language/flows/affected-tools.d.ts +1 -1
  222. package/dist/language/flows/affected-tools.js +49 -30
  223. package/dist/language/flows/change/git-changed.d.ts +2 -1
  224. package/dist/language/flows/change/git-changed.js +19 -2
  225. package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
  226. package/dist/language/flows/fields/flow-secret-field.js +77 -21
  227. package/dist/language/flows/flow-anchor.d.ts +60 -2
  228. package/dist/language/flows/flow-anchor.js +125 -20
  229. package/dist/language/flows/flow-classify.d.ts +20 -0
  230. package/dist/language/flows/flow-classify.js +90 -12
  231. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  232. package/dist/language/flows/flow-cross-step.js +25 -1
  233. package/dist/language/flows/flow-journey.d.ts +38 -4
  234. package/dist/language/flows/flow-journey.js +138 -8
  235. package/dist/language/flows/flow-replay-run.d.ts +0 -16
  236. package/dist/language/flows/flow-replay-run.js +76 -14
  237. package/dist/language/flows/flow-replay-types.d.ts +4 -2
  238. package/dist/language/flows/flow-replay.d.ts +19 -1
  239. package/dist/language/flows/flow-replay.js +169 -52
  240. package/dist/language/flows/flow-replayable.js +7 -1
  241. package/dist/language/flows/flow-start-path.d.ts +34 -5
  242. package/dist/language/flows/flow-start-path.js +202 -15
  243. package/dist/language/flows/flow-step-runners.d.ts +14 -13
  244. package/dist/language/flows/flow-step-runners.js +184 -109
  245. package/dist/language/flows/flow-store-for-session.d.ts +26 -25
  246. package/dist/language/flows/flow-store-for-session.js +51 -5
  247. package/dist/language/flows/flow-tools.js +46 -33
  248. package/dist/language/flows/flows.js +176 -76
  249. package/dist/language/flows/recording/tape/recordings.d.ts +21 -16
  250. package/dist/language/flows/recording/tape/recordings.js +112 -49
  251. package/dist/language/flows/replay.d.ts +36 -12
  252. package/dist/language/flows/replay.js +193 -50
  253. package/dist/language/flows/steps/page-steps.d.ts +68 -0
  254. package/dist/language/flows/steps/page-steps.js +198 -0
  255. package/dist/language/flows/suite/sequential-run.d.ts +68 -0
  256. package/dist/language/flows/suite/sequential-run.js +96 -0
  257. package/dist/language/flows/verify-change-tools.js +26 -15
  258. package/dist/launch-chromium.d.ts +1 -0
  259. package/dist/launch-chromium.js +49 -12
  260. package/dist/memory/cloud/background-sync.d.ts +13 -0
  261. package/dist/memory/cloud/background-sync.js +24 -3
  262. package/dist/memory/cloud/sync-daemon.js +26 -27
  263. package/dist/memory/cloud/sync-screenshots.js +6 -1
  264. package/dist/memory/journal/attach-journal.js +2 -1
  265. package/dist/memory/journal/drive-flow.d.ts +7 -0
  266. package/dist/memory/journal/drive-flow.js +44 -13
  267. package/dist/memory/journal/journal-recorder.d.ts +4 -2
  268. package/dist/memory/journal/session-journal.d.ts +7 -0
  269. package/dist/memory/journal/session-journal.js +58 -10
  270. package/dist/memory/project/root-claims.d.ts +9 -0
  271. package/dist/memory/project/root-claims.js +9 -4
  272. package/dist/portal/bridge/bridge.d.ts +13 -4
  273. package/dist/portal/bridge/bridge.js +10 -9
  274. package/dist/portal/input/download-capture.d.ts +7 -0
  275. package/dist/portal/input/download-capture.js +92 -0
  276. package/dist/portal/input/network-detail.d.ts +6 -0
  277. package/dist/portal/input/network-detail.js +12 -1
  278. package/dist/portal/input/network-mock-tools.js +49 -13
  279. package/dist/portal/input/network-mock.d.ts +8 -0
  280. package/dist/portal/input/network-mock.js +30 -10
  281. package/dist/portal/input/page-capture.d.ts +24 -0
  282. package/dist/portal/input/page-capture.js +33 -0
  283. package/dist/portal/input/real-input.d.ts +35 -18
  284. package/dist/portal/input/real-input.js +99 -33
  285. package/dist/portal/input/viewport-tools.d.ts +8 -0
  286. package/dist/portal/input/viewport-tools.js +81 -21
  287. package/dist/portal/pool/browser-pool.d.ts +16 -30
  288. package/dist/portal/pool/browser-pool.js +64 -52
  289. package/dist/portal/pool/context-permissions.d.ts +34 -17
  290. package/dist/portal/pool/context-permissions.js +94 -1
  291. package/dist/portal/pool/csp-widen.d.ts +62 -0
  292. package/dist/portal/pool/csp-widen.js +132 -0
  293. package/dist/portal/pool/determinism.d.ts +52 -0
  294. package/dist/portal/pool/determinism.js +259 -0
  295. package/dist/portal/pool/lease-emulation.d.ts +34 -1
  296. package/dist/portal/pool/lease-emulation.js +18 -0
  297. package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
  298. package/dist/portal/pool/lease-marker-stamp.js +15 -1
  299. package/dist/portal/pool/lease-page-error.d.ts +56 -0
  300. package/dist/portal/pool/lease-page-error.js +160 -0
  301. package/dist/portal/pool/page-error-journal.d.ts +63 -0
  302. package/dist/portal/pool/page-error-journal.js +208 -0
  303. package/dist/portal/pool/playwright-launcher.js +71 -15
  304. package/dist/portal/pool/pool-contract.d.ts +49 -1
  305. package/dist/portal/pool/pool-lease.d.ts +70 -2
  306. package/dist/portal/pool/pool-lease.js +23 -0
  307. package/dist/portal/pool/zero-install.d.ts +8 -0
  308. package/dist/portal/pool/zero-install.js +91 -1
  309. package/dist/portal/session/dev-server/dev-command.d.ts +11 -2
  310. package/dist/portal/session/dev-server/dev-command.js +110 -16
  311. package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
  312. package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
  313. package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
  314. package/dist/portal/session/dev-server/project-ports.js +62 -0
  315. package/dist/portal/session/facts/departed-network.d.ts +66 -0
  316. package/dist/portal/session/facts/departed-network.js +142 -0
  317. package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
  318. package/dist/portal/session/no-session-diagnosis.js +76 -27
  319. package/dist/portal/session/no-session-next-action.d.ts +14 -0
  320. package/dist/portal/session/no-session-next-action.js +56 -7
  321. package/dist/portal/session/no-session-watch.d.ts +6 -0
  322. package/dist/portal/session/no-session-watch.js +41 -4
  323. package/dist/portal/session/page-commands/undeclared-command.d.ts +9 -3
  324. package/dist/portal/session/page-commands/undeclared-command.js +16 -7
  325. package/dist/portal/session/recorded-gaps.d.ts +1 -1
  326. package/dist/portal/session/recorded-gaps.js +5 -2
  327. package/dist/portal/session/serving-directory.d.ts +10 -0
  328. package/dist/portal/session/serving-directory.js +39 -8
  329. package/dist/portal/session/session-manager.d.ts +10 -0
  330. package/dist/portal/session/session-manager.js +211 -81
  331. package/dist/portal/session/session.js +4 -3
  332. package/dist/portal/session/zero-install-first-call.d.ts +20 -1
  333. package/dist/portal/session/zero-install-first-call.js +34 -2
  334. package/dist/start-wiring.d.ts +30 -2
  335. package/dist/start-wiring.js +74 -9
  336. package/dist/surface/mcp/mcp-proxy.js +10 -2
  337. package/dist/surface/mcp/mcp.js +8 -5
  338. package/dist/surface/mcp/peer/agents-connected.d.ts +6 -0
  339. package/dist/surface/mcp/peer/agents-connected.js +58 -0
  340. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +17 -5
  341. package/dist/surface/mcp/shared-params.d.ts +2 -0
  342. package/dist/surface/mcp/shared-params.js +31 -0
  343. package/dist/surface/tools/act/act-observation.d.ts +40 -2
  344. package/dist/surface/tools/act/act-observation.js +118 -12
  345. package/dist/surface/tools/act/act-preflight.js +38 -18
  346. package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
  347. package/dist/surface/tools/act/act-sequence-retry.js +61 -5
  348. package/dist/surface/tools/act/act-target.js +3 -1
  349. package/dist/surface/tools/act/after-match.d.ts +10 -0
  350. package/dist/surface/tools/act/after-match.js +40 -0
  351. package/dist/surface/tools/act/already-true.js +1 -1
  352. package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
  353. package/dist/surface/tools/act/navigation/reload-result.js +25 -2
  354. package/dist/surface/tools/act/resolve-target.d.ts +17 -1
  355. package/dist/surface/tools/act/resolve-target.js +59 -9
  356. package/dist/surface/tools/act/sequence-grade.d.ts +15 -1
  357. package/dist/surface/tools/act/sequence-grade.js +45 -27
  358. package/dist/surface/tools/act/settle-in-flight.js +27 -15
  359. package/dist/surface/tools/act-command.d.ts +23 -0
  360. package/dist/surface/tools/act-command.js +50 -0
  361. package/dist/surface/tools/act-sequence-tool.js +81 -50
  362. package/dist/surface/tools/act-tools.d.ts +1 -13
  363. package/dist/surface/tools/act-tools.js +83 -95
  364. package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
  365. package/dist/surface/tools/agent/build-staleness.js +1 -1
  366. package/dist/surface/tools/assert/assert-verdict.js +18 -12
  367. package/dist/surface/tools/browser-tools.js +87 -13
  368. package/dist/surface/tools/compare-tools.d.ts +11 -0
  369. package/dist/surface/tools/compare-tools.js +94 -0
  370. package/dist/surface/tools/coverage-tools.js +1 -1
  371. package/dist/surface/tools/dynamic-tools.js +11 -3
  372. package/dist/surface/tools/empty-session-list.d.ts +2 -0
  373. package/dist/surface/tools/empty-session-list.js +4 -2
  374. package/dist/surface/tools/error-recovery.d.ts +12 -1
  375. package/dist/surface/tools/error-recovery.js +131 -40
  376. package/dist/surface/tools/explore-tools.js +1 -1
  377. package/dist/surface/tools/invoke-tool.js +29 -21
  378. package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
  379. package/dist/surface/tools/lease-coverage-tools.js +128 -0
  380. package/dist/surface/tools/lease-hint.d.ts +42 -1
  381. package/dist/surface/tools/lease-hint.js +52 -3
  382. package/dist/surface/tools/lease-readiness.d.ts +47 -0
  383. package/dist/surface/tools/lease-readiness.js +126 -0
  384. package/dist/surface/tools/lease-seed.d.ts +16 -1
  385. package/dist/surface/tools/lease-seed.js +37 -4
  386. package/dist/surface/tools/lease-tools.d.ts +3 -41
  387. package/dist/surface/tools/lease-tools.js +104 -124
  388. package/dist/surface/tools/lease-url-identity.d.ts +46 -0
  389. package/dist/surface/tools/lease-url-identity.js +97 -0
  390. package/dist/surface/tools/merge-tools.js +99 -2
  391. package/dist/surface/tools/merged-name-redirect.js +5 -1
  392. package/dist/surface/tools/observe-tools.js +15 -9
  393. package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
  394. package/dist/surface/tools/read/console-pre-attach.js +23 -0
  395. package/dist/surface/tools/read-tools.js +33 -30
  396. package/dist/surface/tools/real-input-attempt.d.ts +0 -8
  397. package/dist/surface/tools/real-input-attempt.js +143 -28
  398. package/dist/surface/tools/repeat-hint.d.ts +40 -0
  399. package/dist/surface/tools/repeat-hint.js +171 -0
  400. package/dist/surface/tools/run-report.d.ts +2 -2
  401. package/dist/surface/tools/run-report.js +27 -15
  402. package/dist/surface/tools/session-ports.d.ts +14 -0
  403. package/dist/surface/tools/session-ports.js +53 -0
  404. package/dist/surface/tools/tool-kit.d.ts +18 -0
  405. package/dist/surface/tools/tool-kit.js +9 -3
  406. package/dist/surface/tools/tool-surface.js +0 -1
  407. package/dist/surface/tools/tools.js +54 -18
  408. package/dist/surface/tools/type-spelling.d.ts +13 -0
  409. package/dist/surface/tools/type-spelling.js +36 -0
  410. package/dist/telemetry/agent-client.d.ts +6 -0
  411. package/dist/telemetry/agent-client.js +10 -0
  412. package/dist/telemetry/argument-shape.js +17 -7
  413. package/dist/telemetry/cli-telemetry.d.ts +10 -1
  414. package/dist/telemetry/cli-telemetry.js +15 -0
  415. package/dist/telemetry/init-telemetry.d.ts +2 -1
  416. package/dist/telemetry/init-telemetry.js +47 -2
  417. package/dist/telemetry/install-trace.js +23 -4
  418. package/dist/telemetry/onboarding-funnel.d.ts +19 -2
  419. package/dist/telemetry/onboarding-funnel.js +25 -3
  420. package/dist/telemetry/proxy-telemetry.d.ts +2 -1
  421. package/dist/telemetry/proxy-telemetry.js +2 -2
  422. package/dist/telemetry/session-metrics.d.ts +1 -1
  423. package/dist/telemetry/session-metrics.js +67 -27
  424. package/dist/telemetry/telemetry.d.ts +3 -1
  425. package/dist/telemetry/telemetry.js +60 -28
  426. package/dist/telemetry/tool-refused.d.ts +6 -3
  427. package/dist/telemetry/tool-refused.js +15 -3
  428. package/docs/actions.mdx +3 -3
  429. package/docs/agent-cheatsheet.md +1 -1
  430. package/docs/architecture.md +3 -6
  431. package/docs/capabilities.mdx +61 -1
  432. package/docs/cli/affected.mdx +9 -7
  433. package/docs/cli/cloud.mdx +3 -3
  434. package/docs/cli/compare.mdx +94 -0
  435. package/docs/cli/doctor.mdx +41 -2
  436. package/docs/cli/grade.mdx +97 -0
  437. package/docs/cli/help.mdx +4 -2
  438. package/docs/cli/license.mdx +13 -12
  439. package/docs/cli/restart.mdx +7 -5
  440. package/docs/cli/rollback.mdx +5 -5
  441. package/docs/cli/setup.mdx +13 -2
  442. package/docs/cli/status.mdx +4 -0
  443. package/docs/cli/stop.mdx +3 -3
  444. package/docs/cli/update.mdx +2 -2
  445. package/docs/cli/verify.mdx +23 -1
  446. package/docs/cli.mdx +34 -24
  447. package/docs/deploy-checks.md +25 -0
  448. package/docs/desktop.mdx +1 -1
  449. package/docs/enterprise.md +17 -22
  450. package/docs/environment.mdx +1 -0
  451. package/docs/first-drive.md +1 -1
  452. package/docs/flows.md +11 -1
  453. package/docs/frameworks.mdx +9 -9
  454. package/docs/getting-started.md +1 -1
  455. package/docs/install-manual.mdx +16 -1
  456. package/docs/license-activation.mdx +3 -3
  457. package/docs/packages/engine.mdx +1 -1
  458. package/docs/packages/server.mdx +1 -1
  459. package/docs/packages/tauri.mdx +1 -1
  460. package/docs/packages/test.mdx +1 -1
  461. package/docs/packages.mdx +1 -1
  462. package/docs/platform-integration.md +1 -1
  463. package/docs/predicates.mdx +1 -1
  464. package/docs/quickstart.mdx +68 -49
  465. package/docs/skill-setup.md +1 -1
  466. package/docs/skill.md +14 -13
  467. package/docs/telemetry.md +6 -4
  468. package/docs/tools/navigate.mdx +1 -1
  469. package/docs/tools/verify.mdx +4 -1
  470. package/docs/troubleshooting.mdx +1 -1
  471. package/docs/usage.md +17 -14
  472. package/package.json +18 -14
  473. package/LICENSE-ENTERPRISE +0 -37
  474. package/dist/features/ee/audit-log.d.ts +0 -14
  475. package/dist/features/ee/audit-log.js +0 -11
  476. package/dist/features/exhaust/exhaust-run.d.ts +0 -33
  477. package/dist/features/exhaust/exhaust-run.js +0 -82
@@ -9,25 +9,17 @@
9
9
  import { expectedElementTestid } from '@reticlehq/core';
10
10
  import { AnchorKind, DEGRADED_ANCHOR_ROLE, DOCUMENT_ANCHOR_ROLE, DriftReason, PredicateKind, QueryBy, ReplayStatus, ReticleCommand, } from '@reticlehq/core';
11
11
  import { ReticleTool, isGlobalPressCall } from '@reticlehq/core';
12
- import { replayActionArgs } from './replay.js';
13
- import { isStaleRefError } from '../../surface/tools/act/act-sequence-retry.js';
12
+ import { namelessRefs, replayActionArgs } from './replay.js';
13
+ import { burstGap, holdGap, isStaleRefError, pacedArgs, } from '../../surface/tools/act/act-sequence-retry.js';
14
14
  import { waitForReaction } from '../../surface/tools/act/react-grace.js';
15
- import { unreachableTargetNote } from '../../surface/tools/act/act-observation.js';
15
+ import { unreachableSubStepDrift, unreachableTargetNote, } from '../../surface/tools/act/act-observation.js';
16
16
  import { anchorFieldName } from './flows.js';
17
- import { ambiguityDrift, refFor, anchorLabel, componentLabel, componentQueryArgs, expectElementDrift, queryOnce, reresolved, resolveQuery, testidDrift, } from './flow-anchor.js';
17
+ import { ambiguityDrift, refFor, anchorLabel, componentLabel, componentQueryArgs, expectElementDrift, queryOnce, reresolved, resolveRoleAnchor, roleQueryArgs, resolveQuery, roleAnchorLabel, testidDrift, } from './flow-anchor.js';
18
18
  import { nearestRoleName } from './role-anchor-nearest.js';
19
19
  import { roleDriftReason } from '../../judgement/outcome/role-drift-reason.js';
20
+ import { captureBaselines } from '@reticlehq/engine/evidence/baseline.js';
20
21
  /** The live act_sequence tool gives each step this transport budget when none was supplied. */
21
22
  export const DEFAULT_SEQUENCE_STEP_TIMEOUT_MS = 8_000;
22
- /** Query args for a NAMED role anchor — the handle that identifies an instance, not a JSX site. */
23
- function roleQueryArgs(anchor) {
24
- return {
25
- by: QueryBy.ROLE,
26
- value: anchor.role,
27
- ...(anchor.name === undefined ? {} : { name: anchor.name }),
28
- };
29
- }
30
- /** Run one role+name-anchored step: re-resolve via QUERY by:'role', ACT on the live ref, else drift. */
31
23
  /**
32
24
  * The controls actually on the page, as role + name, for the nearest-match scan.
33
25
  *
@@ -36,8 +28,9 @@ function roleQueryArgs(anchor) {
36
28
  */
37
29
  async function nearestRoleNameOnPage(session, anchor) {
38
30
  const wanted = 'string' === typeof anchor.name ? anchor.name : undefined;
39
- if (wanted === undefined)
31
+ if (wanted === undefined) {
40
32
  return null;
33
+ }
41
34
  try {
42
35
  // Straight off the QUERY result: resolveQuery reduces to refs, and the NAMES are what this needs.
43
36
  const result = await session.command(ReticleCommand.QUERY, {
@@ -48,8 +41,9 @@ async function nearestRoleNameOnPage(session, anchor) {
48
41
  const elements = 'object' === typeof payload && payload !== null
49
42
  ? payload.elements
50
43
  : undefined;
51
- if (!Array.isArray(elements))
44
+ if (!Array.isArray(elements)) {
52
45
  return null;
46
+ }
53
47
  const candidates = elements
54
48
  .map((element) => 'object' === typeof element && element !== null
55
49
  ? element.name
@@ -64,27 +58,33 @@ async function nearestRoleNameOnPage(session, anchor) {
64
58
  }
65
59
  }
66
60
  export async function runRoleStep(session, step, index, anchor, confirmDangerous, sleep) {
67
- const label = `${anchor.role} "${String(anchor.name)}"`;
68
- const { refs } = await resolveQuery(session, roleQueryArgs(anchor), sleep);
61
+ const label = roleAnchorLabel(anchor);
62
+ const { refs } = await resolveRoleAnchor(session, anchor, sleep);
69
63
  /*
70
- * More than one match is DRIFT, not a guess. This path took `refs[0]`, dispatched, and returned
71
- * `ok: true`, so a flow recorded against "View result" on row 3 of a table replayed by clicking row
72
- * 1 and PASSED whenever every row shared the consequence (the same GET returning 200). The verdict
73
- * reads `drift` and `ok` and nothing else, so a green here is indistinguishable from a replay that
74
- * did what it did before.
75
- *
76
- * A role+name anchor is MORE likely to land here than a testid is: `button "View result"` is
77
- * user-visible text, and a table repeats it by nature, so several matches is the expected shape of
78
- * a recorded row action rather than an accident.
79
- *
80
- * The check itself lives in `ambiguityDrift`, shared with the testid and component runners: written
81
- * per-runner, this rule got applied to two anchor kinds and forgotten for the third.
64
+ * More than one match is DRIFT, not a guess: taking `refs[0]` replayed a recorded row action on
65
+ * row 1 and passed whenever every row shared the consequence. `ambiguityDrift` holds the rule,
66
+ * shared with the testid and component runners.
82
67
  */
83
68
  const ambiguous = ambiguityDrift(anchor, refs);
84
69
  if (ambiguous !== null) {
85
70
  return { step: index, tool: step.tool, anchor: label, ok: false, drift: ambiguous };
86
71
  }
87
72
  const ref = refFor(anchor, refs);
73
+ if (ref === undefined && anchor.name === undefined) {
74
+ // There is no name to suggest a nearest match from; say what was looked for and what was found.
75
+ return {
76
+ step: index,
77
+ tool: step.tool,
78
+ anchor: label,
79
+ ok: false,
80
+ drift: {
81
+ reasonKind: DriftReason.COMPONENT_NOT_FOUND,
82
+ reason: `no ${anchor.role} with no accessible name was found at the recorded position (${label}); the page has ${String(refs.length)} such control(s). Give the control an accessible name or a test id, or re-record the step`,
83
+ anchor: label,
84
+ nearest: null,
85
+ },
86
+ };
87
+ }
88
88
  if (ref === undefined) {
89
89
  // `nearest` used to be the literal null, so heal answered "no nearest match cleared the
90
90
  // confidence floor" for EVERY role-anchored drift — a structural limit reported as a judgement
@@ -115,7 +115,7 @@ export async function runRoleStep(session, step, index, anchor, confirmDangerous
115
115
  * re-make the guess the check above exists to refuse — one layer deeper, where the caller cannot
116
116
  * see it. `reresolved` is the same rule as that check, so the two cannot disagree.
117
117
  */
118
- async () => reresolved(anchor, (await resolveQuery(session, roleQueryArgs(anchor), sleep)).refs));
118
+ async () => reresolved(anchor, (await resolveRoleAnchor(session, anchor, sleep)).refs));
119
119
  }
120
120
  /**
121
121
  * `assertActionAllowed`'s refusal — "potentially destructive action blocked; retry with
@@ -132,8 +132,9 @@ export async function runRoleStep(session, step, index, anchor, confirmDangerous
132
132
  */
133
133
  const DESTRUCTIVE_ACTION_PATTERN = /potentially destructive (?:\w+ )*(?:action|tool) blocked/i;
134
134
  function replayDestructiveActionHint(rawError) {
135
- if (!DESTRUCTIVE_ACTION_PATTERN.test(rawError))
135
+ if (!DESTRUCTIVE_ACTION_PATTERN.test(rawError)) {
136
136
  return rawError;
137
+ }
137
138
  return (`${rawError} — this is a flow REPLAY: a step's own args.confirmDangerous is stripped before ` +
138
139
  'dispatch and never persists, so editing the flow file does nothing. Pass it at the call level ' +
139
140
  `instead: ${ReticleTool.FLOW_REPLAY} { confirmDangerous: true } acknowledges every flagged step ` +
@@ -211,15 +212,17 @@ reresolve) {
211
212
  if (fresh !== undefined && 'drift' in fresh) {
212
213
  return { step: index, tool: step.tool, anchor: label, ok: false, drift: fresh.drift };
213
214
  }
214
- if (fresh !== undefined)
215
+ if (fresh !== undefined) {
215
216
  act = await dispatch(fresh.ref);
217
+ }
216
218
  }
217
219
  const result = { step: index, tool: step.tool, anchor: label, ok: act.ok };
218
- if (!act.ok)
220
+ if (!act.ok) {
219
221
  result.error = replayDestructiveActionHint(act.error ?? 'command failed');
222
+ }
220
223
  // A click no person could have made decides nothing, on replay as on a live act.
221
224
  const unreachable = act.ok ? unreachableTargetNote(act.result) : undefined;
222
- if (unreachable !== undefined)
225
+ if (unreachable !== undefined) {
223
226
  return {
224
227
  ...result,
225
228
  ok: false,
@@ -230,6 +233,7 @@ reresolve) {
230
233
  nearest: null,
231
234
  },
232
235
  };
236
+ }
233
237
  return result;
234
238
  }
235
239
  /** How long to let the app finish re-rendering before the one retry. Matches the sequence path. */
@@ -266,7 +270,9 @@ export async function runComponentStep(session, step, index, anchor, confirmDang
266
270
  if (ambiguous !== null) {
267
271
  return { step: index, tool: step.tool, anchor: label, ok: false, drift: ambiguous };
268
272
  }
269
- const ref = refs[0] ?? '';
273
+ // The only match, or the recorded position (`ambiguityDrift` has cleared every other case). It took
274
+ // `refs[0]`, so a recorded position that passed the check still acted on the first match.
275
+ const ref = refFor(anchor, refs) ?? '';
270
276
  // Shared with the role path so both get the same action window AND the same stale-ref retry —
271
277
  // this used to dispatch inline, which is how one anchor kind could gain a fix the other lacked.
272
278
  return await actOnResolvedRef(session, step, index, label, ref, confirmDangerous, anchorFieldName(anchor),
@@ -340,10 +346,12 @@ export function degradedStepResult(step, index, label) {
340
346
  * the step runner's own resolution asking the same thing of the same anchor.
341
347
  */
342
348
  export function anchorQueryArgs(anchor) {
343
- if (anchor.kind === AnchorKind.TESTID)
349
+ if (anchor.kind === AnchorKind.TESTID) {
344
350
  return { by: QueryBy.TESTID, value: anchor.value };
345
- if (anchor.kind === AnchorKind.COMPONENT)
351
+ }
352
+ if (anchor.kind === AnchorKind.COMPONENT) {
346
353
  return componentQueryArgs(anchor);
354
+ }
347
355
  if (anchor.kind === AnchorKind.ROLE && !isDegradedAnchor(anchor) && !isDocumentAnchor(anchor)) {
348
356
  return roleQueryArgs(anchor);
349
357
  }
@@ -388,8 +396,9 @@ const staleTargetReason = (step) => `step ${String(step)} could not be acted on:
388
396
  */
389
397
  function staleSubStep(error, live, from, to) {
390
398
  const named = /ref '([^']*)'/.exec(error)?.[1];
391
- if (named === undefined)
399
+ if (named === undefined) {
392
400
  return undefined;
401
+ }
393
402
  const sharing = live.flatMap((one, at) => at >= from && at < to && one.ref === named ? [at] : []);
394
403
  const [only] = sharing;
395
404
  return 1 === sharing.length ? only : undefined;
@@ -407,17 +416,27 @@ function staleSubStep(error, live, from, to) {
407
416
  * Left alone, so the ordinary verdict stands, when anything else is in the run: a drifted anchor is
408
417
  * drift whatever else happened, and a failure that is not staleness is a failure.
409
418
  */
410
- export function staleTargetResult(name, steps) {
419
+ export function staleTargetResult(name, steps,
420
+ /**
421
+ * `startControlRequired`: replay loaded the start page itself, with no redirect, and the caller
422
+ * asked that a missing step 1 control fail. Then the miss means the app does not offer the control
423
+ * the journey starts with (#2061), so it stays drift. Without it, the miss is not proved.
424
+ */
425
+ options = {}) {
411
426
  // A target no person could reach is the same kind of answer: nothing about the app was decided.
412
- const unreachable = unreachableTargetResult(name, steps) ?? unreachedStepResult(name, steps);
413
- if (unreachable !== undefined)
427
+ const unreached = true === options.startControlRequired ? undefined : unreachedStepResult(name, steps);
428
+ const unreachable = unreachableTargetResult(name, steps) ?? unreached;
429
+ if (unreachable !== undefined) {
414
430
  return unreachable;
415
- if (steps.some((step) => step.drift !== undefined))
431
+ }
432
+ if (steps.some((step) => step.drift !== undefined)) {
416
433
  return undefined;
434
+ }
417
435
  const failed = steps.filter((step) => !step.ok);
418
436
  const [only] = failed;
419
- if (failed.length !== 1 || only === undefined || !isStaleRefError(only.error))
437
+ if (failed.length !== 1 || only === undefined || !isStaleRefError(only.error)) {
420
438
  return undefined;
439
+ }
421
440
  return {
422
441
  name,
423
442
  status: ReplayStatus.UNVERIFIABLE,
@@ -433,8 +452,9 @@ export function staleTargetResult(name, steps) {
433
452
  */
434
453
  export function unreachableTargetResult(name, steps) {
435
454
  const step = steps.find((s) => DriftReason.TARGET_UNREACHABLE === s.drift?.reasonKind);
436
- if (step?.drift === undefined)
455
+ if (step?.drift === undefined) {
437
456
  return undefined;
457
+ }
438
458
  return {
439
459
  name,
440
460
  status: ReplayStatus.UNVERIFIABLE,
@@ -456,7 +476,8 @@ const ANCHOR_MISSES = new Set([
456
476
  * FAIL, and the dashboard showed a failure for a journey that was never tested.
457
477
  *
458
478
  * Only when no earlier step ran. A control that is missing after earlier steps acted is on a page
459
- * those steps made, so it stays drift, and heal reads it. A step that acted and whose expectation
479
+ * those steps made, so it stays drift, and heal reads it. An ambiguous control is the exception: it
480
+ * is on the page, and the replay cannot choose, so it is not proved after any number of steps. A step that acted and whose expectation
460
481
  * did not hold is a consequence drift and stays a failure. The failing step keeps its index and
461
482
  * its `drift.reasonKind`, which the synced run carries in `stepResults`.
462
483
  */
@@ -464,14 +485,31 @@ function unreachedStepResult(name, steps) {
464
485
  const at = steps.findIndex((s) => !s.ok);
465
486
  const step = steps[at];
466
487
  const drift = step?.drift;
467
- if (step === undefined || drift === undefined || !ANCHOR_MISSES.has(drift.reasonKind))
488
+ if (step === undefined || drift === undefined || !ANCHOR_MISSES.has(drift.reasonKind)) {
468
489
  return undefined;
469
- if (steps.slice(0, at).some((s) => s.ok))
490
+ }
491
+ const acted = steps.slice(0, at).some((s) => s.ok);
492
+ // An anchor that several live elements match is on the page, so the earlier steps did not lose it.
493
+ // The replay cannot choose one, so nothing about the app was decided, whatever ran before.
494
+ if (DriftReason.ANCHOR_AMBIGUOUS === drift.reasonKind && acted) {
495
+ return {
496
+ name,
497
+ status: ReplayStatus.UNVERIFIABLE,
498
+ steps: [...steps],
499
+ unverifiable: {
500
+ reason: `step ${String(step.step + 1)}: ${drift.reason}. The replay stopped there, so this run ` +
501
+ `proved nothing about the app. Narrow the anchor on this step.`,
502
+ },
503
+ };
504
+ }
505
+ if (acted) {
470
506
  return undefined;
507
+ }
471
508
  // ponytail: one close, unambiguous survivor reads as a rename (heal's case), so it stays drift. A
472
509
  // page with exactly one sibling of the missing control (`toggle-1` for `toggle-3`) reads the same.
473
- if (drift.nearest !== null && true !== drift.ambiguous)
510
+ if (drift.nearest !== null && true !== drift.ambiguous) {
474
511
  return undefined;
512
+ }
475
513
  return {
476
514
  name,
477
515
  status: ReplayStatus.UNVERIFIABLE,
@@ -485,35 +523,40 @@ function unreachedStepResult(name, steps) {
485
523
  }
486
524
  /**
487
525
  * Run an act_sequence step: resolve every sub-step's OWN anchor, then dispatch the whole thing as one
488
- * ACT_SEQUENCE — the same shape `replayProgram` already uses, so a recorded sequence and a saved one
489
- * execute identically.
490
- *
491
- * `replayFlow` had no sequence branch at all. It dispatches on `anchor.kind`, so a saved sequence fell
492
- * to the testid runner and ran ONE act with `action: ''` (a saved sequence carries no top-level
493
- * action) — sub-steps 2..n never executed. Fixing the anchor without this would have turned a visible
494
- * drift into a silent partial replay reporting ok.
526
+ * ACT_SEQUENCE, the shape `replayProgram` uses, so a recorded sequence and a saved one run the same.
495
527
  */
496
528
  export async function runSequenceStep(session, step, index, subs, confirmDangerous, sleep,
497
529
  /** Testids whose presence is deliberately NOT asserted — the LLM-output case. */
498
530
  dynamic = new Set(),
499
- /**
500
- * Judge one sub-step's `expect` over the window from `since`, which opens just before that
501
- * sub-step's dispatch. Omitted for a setup step, whose consequences are not checked.
502
- */
531
+ /** Judge a sub-step's `expect` from `since`, just before its dispatch, against the `before`
532
+ * reading; omitted for a setup step, whose consequences are not checked. */
503
533
  judge) {
534
+ const gap = burstGap(step.burstMs);
535
+ let lastSent;
504
536
  /**
505
537
  * Find one sub-step's element as the page is NOW. Either the live step to dispatch, or the step
506
538
  * result that ends the run: a degraded anchor, or one that is no longer on the page.
507
539
  *
508
- * `afterRerender` is the second look, taken because the page re-rendered under a sub-step. An anchor
509
- * that now matches several elements is drift there, as it is for a single step's retry: the first of
510
- * the new matches may be an element the recording did not mean, and acting on it would report a pass
511
- * for a click nobody can attribute. The first look keeps taking the first match.
540
+ * An anchor that matches several elements is drift, on the first look and on the look after a
541
+ * re-render alike, as it is for a single step: the first match may be an element the recording did
542
+ * not mean, and acting on it reports a result for a click nobody can attribute.
512
543
  *
513
544
  * `ahead` is a look at a sub-step before its turn. It is one query with no settle. An anchor that is
514
545
  * not on the page yet is `pending`, not drift, because an earlier sub-step can render it.
515
546
  */
516
- const resolveSub = async (sub, subIndex, afterRerender = false, ahead = false) => {
547
+ const lookUpSub = async (anchor, queryArgs, ahead) => {
548
+ const nameless = AnchorKind.ROLE === anchor.kind && anchor.name === undefined;
549
+ if (ahead) {
550
+ return nameless
551
+ ? { refs: namelessRefs(await session.command(ReticleCommand.QUERY, queryArgs)) }
552
+ : await queryOnce(session, queryArgs);
553
+ }
554
+ if (AnchorKind.ROLE === anchor.kind) {
555
+ return await resolveRoleAnchor(session, anchor, sleep);
556
+ }
557
+ return await resolveQuery(session, queryArgs, sleep);
558
+ };
559
+ const resolveSub = async (sub, subIndex, ahead = false) => {
517
560
  const label = `${anchorLabel(sub.anchor)} (sub-step ${String(subIndex)})`;
518
561
  if (isDocumentPressStep(sub)) {
519
562
  // Dispatched as it ran live, at the document with no ref (#1559).
@@ -521,24 +564,31 @@ judge) {
521
564
  live: {
522
565
  ref: '',
523
566
  action: sub.action ?? '',
524
- args: replayActionArgs(sub.args, confirmDangerous, undefined),
567
+ args: pacedArgs(replayActionArgs(sub.args, confirmDangerous, undefined), gap),
525
568
  },
526
569
  };
527
570
  }
528
571
  const queryArgs = anchorQueryArgs(sub.anchor);
529
- if (null === queryArgs)
572
+ if (null === queryArgs) {
530
573
  return { stop: degradedStepResult(step, index, label) };
531
- const { refs, hint } = ahead
532
- ? await queryOnce(session, queryArgs)
533
- : await resolveQuery(session, queryArgs, sleep);
534
- const ambiguous = afterRerender ? ambiguityDrift(sub.anchor, refs) : null;
574
+ }
575
+ // A role anchor is found as a single step finds it, a nameless one among the nameless controls
576
+ // of its role (#2084: querying every control sent a file upload to the first named button).
577
+ const { refs, hint } = await lookUpSub(sub.anchor, queryArgs, ahead);
578
+ // Several matches with no usable recorded position is drift on every look, as in the single-step
579
+ // runners. The first look used to take `refs[0]`: a click recorded on one item of a list (every
580
+ // item from one JSX line) replayed on another item and failed a correct app. A look ahead is not
581
+ // a verdict, and an earlier sub-step can still change what is on the page.
582
+ const ambiguous = ahead ? null : ambiguityDrift(sub.anchor, refs);
535
583
  if (ambiguous !== null) {
536
584
  return { stop: { step: index, tool: step.tool, anchor: label, ok: false, drift: ambiguous } };
537
585
  }
538
- const ref = afterRerender ? refFor(sub.anchor, refs) : refs[0];
586
+ // The only match, or the recorded position while the page has exactly as many namesakes.
587
+ const ref = refFor(sub.anchor, refs);
539
588
  if (ref === undefined) {
540
- if (ahead)
589
+ if (ahead) {
541
590
  return { pending: true };
591
+ }
542
592
  return {
543
593
  stop: {
544
594
  step: index,
@@ -560,9 +610,8 @@ judge) {
560
610
  live: {
561
611
  ref,
562
612
  action: sub.action ?? '',
563
- // Each sub-step carries its OWN anchor, so each gets its own field name. A sequence that ends
564
- // in a login is the shape this was reported on, and the sub-step is where the fill lives.
565
- args: replayActionArgs(sub.args, confirmDangerous, anchorFieldName(sub.anchor)),
613
+ // Each sub-step carries its OWN anchor, so each gets its own field name (a login's fill).
614
+ args: pacedArgs(replayActionArgs(sub.args, confirmDangerous, anchorFieldName(sub.anchor)), gap),
566
615
  },
567
616
  };
568
617
  };
@@ -580,13 +629,16 @@ judge) {
580
629
  const resolveFrom = async (from) => {
581
630
  for (let at = from; at < subs.length; at += 1) {
582
631
  const sub = subs[at];
583
- if (sub === undefined)
632
+ if (sub === undefined) {
584
633
  continue;
585
- const resolved = await resolveSub(sub, at, false, at > from);
586
- if ('pending' in resolved)
634
+ }
635
+ const resolved = await resolveSub(sub, at, at > from);
636
+ if ('pending' in resolved) {
587
637
  return { end: at };
588
- if ('stop' in resolved)
638
+ }
639
+ if ('stop' in resolved) {
589
640
  return resolved;
641
+ }
590
642
  live[at] = resolved.live;
591
643
  }
592
644
  return { end: subs.length };
@@ -623,18 +675,20 @@ judge) {
623
675
  *
624
676
  * The page names the ref it could not find, which says WHICH sub-step failed and so which ones
625
677
  * already ran, as long as no other sub-step holds the same ref. Only the rest is resolved again and
626
- * sent, so nothing acts twice: starting over is what turns one click into two. A stale ref is the
627
- * only failure retried, and the retry goes on only while it gets further. A sub-step that is stale
628
- * again at the same point is a target that does not hold still, and the run says so, with how far
629
- * it got.
678
+ * sent, so nothing acts twice. Only a stale ref is retried, and only while the retry gets further:
679
+ * stale again at the same point is a target that does not hold still, and the run says so.
630
680
  */
631
681
  const dispatchFrom = async (from, to) => {
632
682
  let start = from;
633
683
  let staleBefore = -1;
634
684
  for (;;) {
635
685
  const act = await send(live.slice(start, to));
636
- if (act.ok)
637
- return true;
686
+ if (act.ok) {
687
+ // A sub-step no person could have clicked decides nothing (#2113).
688
+ const drift = unreachableSubStepDrift(act.result, (at) => `${anchorLabel(subs[start + at]?.anchor ?? step.anchor)} (sub-step ${String(start + at)})`);
689
+ Object.assign(result, drift === undefined ? {} : { ok: false, anchor: drift.anchor, drift });
690
+ return drift === undefined;
691
+ }
638
692
  const error = act.error ?? 'command failed';
639
693
  if (!isStaleRefError(error)) {
640
694
  result.ok = false;
@@ -656,19 +710,22 @@ judge) {
656
710
  await waitForReaction(session, 0, STALE_REF_GRACE_MS, { sleep });
657
711
  for (let at = staleAt; at < to; at += 1) {
658
712
  const sub = subs[at];
659
- if (sub === undefined)
713
+ if (sub === undefined) {
660
714
  continue;
661
- const resolved = await resolveSub(sub, at, true);
715
+ }
716
+ const resolved = await resolveSub(sub, at);
662
717
  if ('stop' in resolved) {
663
718
  result.ok = false;
664
719
  result.anchor = resolved.stop.anchor;
665
- if (resolved.stop.drift !== undefined)
720
+ if (resolved.stop.drift !== undefined) {
666
721
  result.drift = resolved.stop.drift;
722
+ }
667
723
  return false;
668
724
  }
669
725
  // A second look settles, so it is never `pending`.
670
- if ('live' in resolved)
726
+ if ('live' in resolved) {
671
727
  live[at] = resolved.live;
728
+ }
672
729
  }
673
730
  start = staleAt;
674
731
  }
@@ -687,8 +744,9 @@ judge) {
687
744
  */
688
745
  const assertDeclared = async (testid) => {
689
746
  const found = await resolveQuery(session, { by: QueryBy.TESTID, value: testid }, sleep);
690
- if (0 < found.refs.length)
747
+ if (0 < found.refs.length) {
691
748
  return true;
749
+ }
692
750
  result.ok = false;
693
751
  result.drift = expectElementDrift(testid, found.hint);
694
752
  return false;
@@ -697,48 +755,65 @@ judge) {
697
755
  * A sequence that declares nothing goes as ONE batch — the cheap path, and the common one.
698
756
  *
699
757
  * When a sub-step DOES declare, the sub-steps are walked and each expectation is checked right
700
- * after its own action. Checking them all at the end, which is what replay used to do, let "click
701
- * Pay, and the receipt appears" pass because a LATER sub-step produced the receipt: true by the
702
- * time anybody looked, and no longer a claim about this action. The live act path never had that
703
- * hole, because it asserts before moving on; this is replay catching up to it.
704
- *
705
- * The walk costs a round trip per sub-step, so only a flow that declared something pays for it.
758
+ * after its own action, as live: checked at the end, "click Pay, and the receipt appears" passed
759
+ * because a LATER sub-step produced the receipt. A sequence driven with `burst` is walked too, one
760
+ * gap between actions, as live: at the default settle a timing-sensitive page answered differently
761
+ * (#2130). The walk costs a round trip per sub-step, so only these sequences pay for it.
706
762
  */
707
763
  const judged = (sub) => judge !== undefined && sub.expect !== undefined;
708
- const declaring = subs.some((sub) => declaredBy(sub) !== undefined || judged(sub));
764
+ const declaring = gap !== undefined || subs.some((sub) => declaredBy(sub) !== undefined || judged(sub));
765
+ // Sub-steps completed with their checks held, for the score; a failed batch leaves its own unknown.
766
+ let completed = 0;
767
+ const finish = (r) => ({
768
+ ...r,
769
+ subSteps: {
770
+ passed: r.ok && r.drift === undefined ? subs.length : completed,
771
+ total: subs.length,
772
+ },
773
+ });
709
774
  for (let next = 0; next < subs.length;) {
710
775
  const stretch = await resolveFrom(next);
711
- if ('stop' in stretch)
712
- return stretch.stop;
776
+ if ('stop' in stretch) {
777
+ return finish(stretch.stop);
778
+ }
713
779
  const { end } = stretch;
714
780
  if (!declaring) {
715
- if (!(await dispatchFrom(next, end)))
716
- return result;
781
+ if (!(await dispatchFrom(next, end))) {
782
+ return finish(result);
783
+ }
784
+ completed = end;
717
785
  }
718
786
  else {
719
787
  for (let at = next; at < end; at += 1) {
720
788
  const sub = subs[at];
721
- if (sub === undefined)
789
+ if (sub === undefined) {
722
790
  continue;
791
+ }
792
+ await holdGap(() => session.elapsed(), lastSent, gap, sleep);
793
+ const before = judged(sub) ? await captureBaselines(session, sub.expect) : undefined;
723
794
  const since = session.elapsed();
724
- if (!(await dispatchFrom(at, at + 1)))
725
- return result;
795
+ lastSent = since;
796
+ if (!(await dispatchFrom(at, at + 1))) {
797
+ return finish(result);
798
+ }
726
799
  const testid = declaredBy(sub);
727
- if (testid !== undefined && !(await assertDeclared(testid)))
728
- return result;
800
+ if (testid !== undefined && !(await assertDeclared(testid))) {
801
+ return finish(result);
802
+ }
729
803
  // The claim is about THIS sub-step: a later one has not run, and an earlier one is before
730
804
  // `since`. Judged at the end, a middle route expect failed and a late claim passed early.
731
805
  const drift = judge !== undefined && sub.expect !== undefined
732
- ? await judge(sub.expect, since)
806
+ ? await judge(sub.expect, since, before)
733
807
  : undefined;
734
808
  if (drift !== undefined) {
735
809
  result.ok = false;
736
810
  result.drift = drift;
737
- return result;
811
+ return finish(result);
738
812
  }
813
+ completed = at + 1;
739
814
  }
740
815
  }
741
816
  next = end;
742
817
  }
743
- return result;
818
+ return finish(result);
744
819
  }
@@ -1,29 +1,5 @@
1
- /**
2
- * The flow store a SESSION's flows belong in.
3
- *
4
- * `deps.flows` is bound to the daemon's own root at construction, which is right only when the
5
- * daemon happens to have been started in the project it is driving. A user-scoped MCP registration
6
- * is the common case and the editor starts it wherever it likes, so "where the daemon was launched"
7
- * and "which app is being verified" are usually different directories.
8
- *
9
- * This used to live inside flow-tools and was applied at exactly ONE of ten call sites. The result
10
- * is the half-applied state `sessionRoot` warns about in its own comment, reached in practice:
11
- *
12
- * - the HUD listed the daemon repo's flows while driving somebody else's app — Electron and Tauri
13
- * flows shown over a React dashboard, none of which existed in the checkout being verified
14
- * - `flow_replay` answered `flow_not_found` for a flow sitting on disk, because it looked in the
15
- * daemon's root while `reticle verify` (which roots itself at cwd) replayed the same flow fine
16
- * - and the two save paths disagreed with each other, so where a flow landed depended on which
17
- * tool wrote it
18
- *
19
- * Reported as "running the daemon from here but taking the directory from there", which is exactly
20
- * what it was.
21
- *
22
- * Rebinding is cheap — a FlowStore is a filesystem port, a root and a clock — and the common case
23
- * allocates nothing: when no resolver is wired, or it names the root already in use, `deps.flows` is
24
- * returned unchanged.
25
- */
26
1
  import { FlowStore } from './flows.js';
2
+ import type { NamedFlow } from './change/flow-sources.js';
27
3
  import { type ProjectTarget } from '../../memory/project/session-root.js';
28
4
  import type { ProjectId } from '@reticlehq/core';
29
5
  import type { ToolDeps } from '../../surface/tools/tool-kit.js';
@@ -37,3 +13,28 @@ export declare function flowsForSessionId(deps: ToolDeps, sessionId: string | un
37
13
  root: string;
38
14
  projectId: ProjectId | undefined;
39
15
  };
16
+ /** The {name, steps} of every flow in `store` that `projectId` can see. Unreadable flows are left out. */
17
+ export declare function namedFlowsIn(store: FlowStore, projectId: ProjectId | undefined): Promise<NamedFlow[]>;
18
+ /**
19
+ * The saved flows of a session, from the store `flowsForSessionId` resolves, and the project
20
+ * directory that holds that store.
21
+ *
22
+ * `affected` and `change` read flows through here, and `flows` replays from the same store. They
23
+ * used to build their own store from `sessionRoot`, which skips the root a lease claimed when no
24
+ * resolver is wired and no session is named. Then `affected` named a flow that `flows` could not
25
+ * find (#1996). The directory is returned too, so `git diff` runs in the tree the flows belong to.
26
+ *
27
+ * `sessionTarget` declines when several projects are connected and none is named, because a caller
28
+ * that needs no session must still work. These callers answer about ONE project, and that fallback
29
+ * is the daemon's own, so its flows read as the driven tab's (#1550). Here the refusal stands:
30
+ * `resolve` names the connected projects and asks for a `sessionId`. It throws nothing for several
31
+ * tabs of one project, or for no session at all.
32
+ *
33
+ * `unmatchedRoot` is set when the project has no folder: the root is an evidence bucket, not a
34
+ * tree, so there is no `git` to ask and no flows to find.
35
+ */
36
+ export declare function namedFlowsForSession(deps: ToolDeps, sessionId: string | undefined): Promise<{
37
+ flows: NamedFlow[];
38
+ projectDir: string;
39
+ unmatchedRoot?: string;
40
+ }>;