@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
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { z } from 'zod';
2
3
  import { AnchorKind } from '@reticlehq/core';
3
4
  import { CALL_SECRETS_ARG, callSecrets } from '../../../surface/tools/tool-kit.js';
@@ -14,12 +15,15 @@ import { CALL_SECRETS_ARG, callSecrets } from '../../../surface/tools/tool-kit.j
14
15
  * to find the env var because the runner passed a role or a label instead.
15
16
  */
16
17
  export function anchorFieldName(anchor) {
17
- if (AnchorKind.TESTID === anchor.kind)
18
+ if (AnchorKind.TESTID === anchor.kind) {
18
19
  return anchor.value;
19
- if (AnchorKind.ROLE === anchor.kind)
20
+ }
21
+ if (AnchorKind.ROLE === anchor.kind) {
20
22
  return anchor.name;
21
- if (AnchorKind.SIGNAL === anchor.kind)
23
+ }
24
+ if (AnchorKind.SIGNAL === anchor.kind) {
22
25
  return anchor.name;
26
+ }
23
27
  return undefined;
24
28
  }
25
29
  /**
@@ -39,6 +43,16 @@ export const SECRET_NAME_ARG = 'secret';
39
43
  export const SECRET_FIELD_ARG = 'secretField';
40
44
  /** The prefix every secret's variable carries. */
41
45
  const SECRET_ENV_PREFIX = 'RETICLE_SECRET_';
46
+ /** Any character outside ASCII. */
47
+ const NON_ASCII = /[^\p{ASCII}]/u;
48
+ /** The word in the variable of a field whose name is not ASCII. */
49
+ const NON_ASCII_FIELD = 'FIELD';
50
+ const NON_ASCII_DIGEST_LENGTH = 8;
51
+ /**
52
+ * The variable name an ASCII field gets. Up to 3.9.0 a non-ASCII field got this name too, and a
53
+ * flow saved then still names it.
54
+ */
55
+ const asciiSecretEnvKey = (field) => `${SECRET_ENV_PREFIX}${field.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase()}`;
42
56
  /**
43
57
  * The environment variable that supplies one redacted field.
44
58
  *
@@ -51,7 +65,16 @@ const SECRET_ENV_PREFIX = 'RETICLE_SECRET_';
51
65
  * rules must not be able to drift. It used to sit in `replay.ts`, one import away from the sentence
52
66
  * saying it had to stay together.
53
67
  */
54
- export const secretEnvKey = (field) => `${SECRET_ENV_PREFIX}${field.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase()}`;
68
+ export function secretEnvKey(field) {
69
+ if (!NON_ASCII.test(field)) {
70
+ return asciiSecretEnvKey(field);
71
+ }
72
+ // A name in another script has no ASCII letters to keep: "Пароль" and "Гасло" both became
73
+ // `RETICLE_SECRET__`, so one field's value was typed into the other. A short hash of the name
74
+ // keeps each field's variable distinct and the same on every machine.
75
+ const digest = createHash('sha256').update(field.normalize('NFC').toLowerCase()).digest('hex');
76
+ return `${SECRET_ENV_PREFIX}${NON_ASCII_FIELD}_${digest.slice(0, NON_ASCII_DIGEST_LENGTH).toUpperCase()}`;
77
+ }
55
78
  /** The schema of that argument, shared by every tool that replays a flow. */
56
79
  export { CALL_SECRETS_ARG };
57
80
  export const callSecretsArg = z
@@ -64,30 +87,35 @@ export const callSecretsArg = z
64
87
  */
65
88
  export function secretValue(envKey, env) {
66
89
  for (const [key, value] of Object.entries(callSecrets() ?? {})) {
67
- if (envKey === (key.startsWith(SECRET_ENV_PREFIX) ? key : secretEnvKey(key)))
90
+ if (envKey === (key.startsWith(SECRET_ENV_PREFIX) ? key : secretEnvKey(key))) {
68
91
  return value;
92
+ }
69
93
  }
70
94
  return env[envKey];
71
95
  }
72
96
  const SECRET_OCCURRENCE = /^(?:[2-9]|[1-9][0-9]+)$/;
73
97
  /** The field's own saved variable name, or its original key for legacy and invalid metadata. */
74
98
  export function secretKeyForFill(args, field) {
75
- if (args?.['value'] !== REDACTED_FILL)
99
+ if (args?.['value'] !== REDACTED_FILL) {
76
100
  return undefined;
101
+ }
77
102
  const named = args[SECRET_NAME_ARG];
78
103
  const base = secretEnvKey(field);
79
- // A flow can supply this field's occurrence, never an unrelated environment variable.
80
- return 'string' === typeof named &&
81
- named.startsWith(`${base}_`) &&
82
- SECRET_OCCURRENCE.test(named.slice(base.length + 1))
83
- ? named
84
- : base;
104
+ // A flow can supply this field's occurrence, never an unrelated environment variable. The name
105
+ // an earlier version gave a non-ASCII field is still this field's.
106
+ if ('string' !== typeof named) {
107
+ return base;
108
+ }
109
+ const own = (key) => named === key ||
110
+ (named.startsWith(`${key}_`) && SECRET_OCCURRENCE.test(named.slice(key.length + 1)));
111
+ return own(base) || own(asciiSecretEnvKey(field)) ? named : base;
85
112
  }
86
113
  function* secretSteps(steps) {
87
114
  for (const step of steps) {
88
115
  yield step;
89
- if (step.steps !== undefined)
116
+ if (step.steps !== undefined) {
90
117
  yield* secretSteps(step.steps);
118
+ }
91
119
  }
92
120
  }
93
121
  /** Allocate recorded fills' names while preserving legacy placeholders and existing suffixes. */
@@ -96,14 +124,17 @@ export function withDistinctSecretKeys(flow) {
96
124
  const used = new Set();
97
125
  for (const step of secretSteps(flow.steps)) {
98
126
  const field = anchorFieldName(step.anchor);
99
- if (field === undefined)
127
+ if (field === undefined) {
100
128
  continue;
129
+ }
101
130
  const key = secretKeyForFill(step.args, field);
102
- if (key === undefined)
131
+ if (key === undefined) {
103
132
  continue;
133
+ }
104
134
  reserved.add(key);
105
- if (key !== secretEnvKey(field) || step.args?.[SECRET_NAME_ARG] === undefined)
135
+ if (key !== secretEnvKey(field) || step.args?.[SECRET_NAME_ARG] === undefined) {
106
136
  used.add(key);
137
+ }
107
138
  }
108
139
  const assign = (step) => {
109
140
  let out = step;
@@ -119,8 +150,9 @@ export function withDistinctSecretKeys(flow) {
119
150
  occurrence += 1;
120
151
  }
121
152
  used.add(key);
122
- if (key !== base)
153
+ if (key !== base) {
123
154
  out = { ...step, args: { ...step.args, [SECRET_NAME_ARG]: key } };
155
+ }
124
156
  }
125
157
  return step.steps === undefined ? out : { ...out, steps: step.steps.map(assign) };
126
158
  };
@@ -138,23 +170,47 @@ export function withDistinctSecretKeys(flow) {
138
170
  * non-zero length, so an empty variable leaves the placeholder and must count as unsupplied here.
139
171
  * Two different answers to "is this supplied" is how the gate would pass and the replay still fail.
140
172
  */
173
+ function* flattenAll(steps) {
174
+ for (const step of steps) {
175
+ yield step;
176
+ yield* flattenAll(step.steps ?? []);
177
+ }
178
+ }
141
179
  export function unsuppliedSecrets(flow, env) {
142
180
  const out = [];
143
181
  const seen = new Set();
144
182
  for (const step of secretSteps(flow.steps ?? [])) {
145
183
  const field = anchorFieldName(step.anchor);
146
- if (field === undefined)
184
+ if (field === undefined) {
147
185
  continue;
186
+ }
148
187
  const envKey = secretKeyForFill(step.args, field);
149
- if (envKey === undefined)
188
+ if (envKey === undefined) {
150
189
  continue;
190
+ }
151
191
  const supplied = secretValue(envKey, env);
152
- if (supplied !== undefined && supplied.length > 0)
192
+ if (supplied !== undefined && supplied.length > 0) {
153
193
  continue;
154
- if (seen.has(envKey))
194
+ }
195
+ if (seen.has(envKey)) {
155
196
  continue;
197
+ }
156
198
  seen.add(envKey);
157
199
  out.push({ field, envKey });
158
200
  }
201
+ // A request step that acts as another account needs that account's session.
202
+ for (const step of flattenAll(flow.steps ?? [])) {
203
+ const account = step.request?.as;
204
+ if (account === undefined) {
205
+ continue;
206
+ }
207
+ const envKey = secretEnvKey(account);
208
+ const supplied = secretValue(envKey, env);
209
+ if ((supplied !== undefined && supplied.length > 0) || seen.has(envKey)) {
210
+ continue;
211
+ }
212
+ seen.add(envKey);
213
+ out.push({ field: account, envKey });
214
+ }
159
215
  return out;
160
216
  }
@@ -1,4 +1,4 @@
1
- import { AnchorKind, type Drift, type FlowAnchor, type QueryEmptyHint } from '@reticlehq/core';
1
+ import { AnchorKind, type CommandResult, type Drift, type FlowAnchor, type QueryEmptyHint } from '@reticlehq/core';
2
2
  import type { FlowReplaySession, Sleep } from './flow-replay-types.js';
3
3
  /**
4
4
  * Finding a step's anchor again, and saying legibly why it could not be found.
@@ -33,6 +33,29 @@ export declare function nearestTestid(missing: string, present: string[]): strin
33
33
  export declare function nearestIsAmbiguous(missing: string, present: string[]): boolean;
34
34
  /** Build the legible-drift record for a testid anchor that resolved to zero live elements. */
35
35
  export declare function testidDrift(value: string, hint: QueryEmptyHint | undefined): Drift;
36
+ /**
37
+ * How an anchor is ADDRESSED — the sentence that names it, and the label a result row carries.
38
+ *
39
+ * Two different strings, and neither can be derived from the other: `value` is a raw testid for one
40
+ * anchor kind and a human-readable label for the others, so one sentence cannot name both. Calling a
41
+ * role anchor `testid "button "View result""` sends the reader to edit a locator that does not exist
42
+ * on that step, which is the same misdirection the drift reason exists to remove.
43
+ *
44
+ * Derived from the anchor rather than passed in by the caller. The first version of the role fix took
45
+ * a `described` argument and wrote `ambiguousAnchorDrift(label, n, \`role anchor ${label}\`)` — the
46
+ * same string twice, so the `anchor` field and the sentence could drift apart with nothing to catch
47
+ * it, and the next anchor kind would have had to remember the convention on its own.
48
+ */
49
+ /**
50
+ * How a role anchor reads in a message: `button "View result"`, or for a control with no accessible
51
+ * name, `button (no accessible name, #2 of 3)` — never `button "undefined"`.
52
+ */
53
+ export declare function roleAnchorLabel(anchor: {
54
+ role: string;
55
+ name?: string | undefined;
56
+ nth?: number | undefined;
57
+ of?: number | undefined;
58
+ }): string;
36
59
  /**
37
60
  * What the stale-ref retry found when it ran the locator again.
38
61
  *
@@ -106,10 +129,28 @@ export declare function queryOnce(session: FlowReplaySession, queryArgs: Record<
106
129
  refs: string[];
107
130
  hint?: QueryEmptyHint;
108
131
  }>;
109
- export declare function resolveQuery(session: FlowReplaySession, queryArgs: Record<string, unknown>, sleep: Sleep, now?: () => number): Promise<{
132
+ export declare function resolveQuery(session: FlowReplaySession, queryArgs: Record<string, unknown>, sleep: Sleep, now?: () => number, read?: (result: CommandResult) => {
133
+ refs: string[];
134
+ hint?: QueryEmptyHint;
135
+ }): Promise<{
110
136
  refs: string[];
111
137
  hint?: QueryEmptyHint;
112
138
  }>;
139
+ /**
140
+ * Several queries for one control, pooled: each attempt runs every query and joins the refs, so
141
+ * the settle budget is spent once, not once for each query. A role anchor with `alsoRoles` uses it.
142
+ */
143
+ export declare function resolveAnyQuery(session: FlowReplaySession, queries: readonly Record<string, unknown>[], sleep: Sleep, now?: () => number, read?: (result: CommandResult) => {
144
+ refs: string[];
145
+ hint?: QueryEmptyHint;
146
+ }): Promise<{
147
+ refs: string[];
148
+ hint?: QueryEmptyHint;
149
+ }>;
150
+ /** `resolveQuery`'s reader for a nameless role anchor: only the matches with no accessible name. */
151
+ export declare function readNamelessQuery(result: CommandResult): {
152
+ refs: string[];
153
+ };
113
154
  /** Re-resolve a testid anchor. */
114
155
  export declare function resolveTestid(session: FlowReplaySession, value: string, sleep: Sleep): Promise<{
115
156
  refs: string[];
@@ -125,3 +166,20 @@ export declare function anchorLabel(anchor: FlowAnchor): string;
125
166
  export declare function componentQueryArgs(anchor: Extract<FlowAnchor, {
126
167
  kind: typeof AnchorKind.COMPONENT;
127
168
  }>): Record<string, unknown>;
169
+ /** Query args for a NAMED role anchor — the handle that identifies an instance, not a JSX site. */
170
+ export declare function roleQueryArgs(anchor: Extract<FlowAnchor, {
171
+ kind: typeof AnchorKind.ROLE;
172
+ }>): Record<string, unknown>;
173
+ /**
174
+ * Resolve a role anchor. ONE function for the first look and the re-resolve after a stale ref: the
175
+ * re-resolve used to count every control of the role, so a nameless anchor's position came back
176
+ * ambiguous on a working page.
177
+ *
178
+ * A nameless anchor's position counts only the nameless controls of its role (an icon-only edit
179
+ * button is the 2nd nameless button of its row, not the 5th button), so it reads only those.
180
+ */
181
+ export declare function resolveRoleAnchor(session: FlowReplaySession, anchor: Extract<FlowAnchor, {
182
+ kind: typeof AnchorKind.ROLE;
183
+ }>, sleep: Sleep): Promise<{
184
+ refs: string[];
185
+ }>;
@@ -1,5 +1,5 @@
1
1
  import { AnchorKind, DriftReason, ReticleCommand, QueryBy, asRecord, asString, } from '@reticlehq/core';
2
- import { queryRefs } from './replay.js';
2
+ import { namelessRefs, queryRefs } from './replay.js';
3
3
  /**
4
4
  * Finding a step's anchor again, and saying legibly why it could not be found.
5
5
  *
@@ -20,16 +20,18 @@ export function editDistance(a, b) {
20
20
  const cols = t.length + 1;
21
21
  const prev = new Array(cols);
22
22
  const curr = new Array(cols);
23
- for (let j = 0; j < cols; j++)
23
+ for (let j = 0; j < cols; j++) {
24
24
  prev[j] = j;
25
+ }
25
26
  for (let i = 1; i < rows; i++) {
26
27
  curr[0] = i;
27
28
  for (let j = 1; j < cols; j++) {
28
29
  const cost = s[i - 1] === t[j - 1] ? 0 : 1;
29
30
  curr[j] = Math.min((prev[j] ?? 0) + 1, (curr[j - 1] ?? 0) + 1, (prev[j - 1] ?? 0) + cost);
30
31
  }
31
- for (let j = 0; j < cols; j++)
32
+ for (let j = 0; j < cols; j++) {
32
33
  prev[j] = curr[j] ?? 0;
34
+ }
33
35
  }
34
36
  return prev[cols - 1] ?? 0;
35
37
  }
@@ -58,8 +60,9 @@ export function nearestTestid(missing, present) {
58
60
  bestDistance = distance;
59
61
  }
60
62
  }
61
- if (null === best)
63
+ if (null === best) {
62
64
  return null;
65
+ }
63
66
  return bestDistance <= Math.max(missing.length, best.length) / 2 ? best : null;
64
67
  }
65
68
  /**
@@ -90,8 +93,9 @@ const ANCHOR_SETTLE_MAX_ATTEMPTS = 40;
90
93
  /** Extract the live element refs + the zero-match near-miss hint from a QUERY command result. */
91
94
  function readQuery(result) {
92
95
  const refs = queryRefs(result);
93
- if (!result.ok)
96
+ if (!result.ok) {
94
97
  return { refs };
98
+ }
95
99
  const payload = asRecord(result.result);
96
100
  const rawHint = payload['hint'];
97
101
  if ('object' === typeof rawHint && rawHint !== null) {
@@ -117,8 +121,9 @@ function readQuery(result) {
117
121
  * Such a drift is surfaced (with a nearest) but never auto-healed.
118
122
  */
119
123
  export function nearestIsAmbiguous(missing, present) {
120
- if (present.length < 2)
124
+ if (present.length < 2) {
121
125
  return false;
126
+ }
122
127
  let min = Number.POSITIVE_INFINITY;
123
128
  let count = 0;
124
129
  for (const candidate of present) {
@@ -142,8 +147,9 @@ export function testidDrift(value, hint) {
142
147
  anchor: value,
143
148
  nearest: nearestTestid(value, present),
144
149
  };
145
- if (nearestIsAmbiguous(value, present))
150
+ if (nearestIsAmbiguous(value, present)) {
146
151
  drift.ambiguous = true;
152
+ }
147
153
  return drift;
148
154
  }
149
155
  /**
@@ -159,6 +165,19 @@ export function testidDrift(value, hint) {
159
165
  * same string twice, so the `anchor` field and the sentence could drift apart with nothing to catch
160
166
  * it, and the next anchor kind would have had to remember the convention on its own.
161
167
  */
168
+ /**
169
+ * How a role anchor reads in a message: `button "View result"`, or for a control with no accessible
170
+ * name, `button (no accessible name, #2 of 3)` — never `button "undefined"`.
171
+ */
172
+ export function roleAnchorLabel(anchor) {
173
+ if (anchor.name !== undefined) {
174
+ return `${anchor.role} "${anchor.name}"`;
175
+ }
176
+ const at = anchor.nth !== undefined && anchor.of !== undefined
177
+ ? `, #${String(anchor.nth + 1)} of ${String(anchor.of)}`
178
+ : '';
179
+ return `${anchor.role} (no accessible name${at})`;
180
+ }
162
181
  function anchorAddress(anchor) {
163
182
  if (anchor.kind === AnchorKind.TESTID) {
164
183
  return { described: `testid "${anchor.value}"`, label: anchor.value };
@@ -166,7 +185,7 @@ function anchorAddress(anchor) {
166
185
  if (anchor.kind === AnchorKind.ROLE) {
167
186
  // Kind named explicitly: `button "View result"` alone reads as a testid that happens to contain
168
187
  // a space, and the reader would go looking for a locator nothing on the step declares.
169
- const label = `${anchor.role} "${String(anchor.name)}"`;
188
+ const label = roleAnchorLabel(anchor);
170
189
  return { described: `role anchor ${label}`, label };
171
190
  }
172
191
  // A component needs no such qualifier: `componentLabel` already answers
@@ -226,8 +245,9 @@ export function ambiguityDrift(anchor, refs) {
226
245
  * while the page still has exactly as many of them as when it was recorded. Otherwise undefined.
227
246
  */
228
247
  export function refFor(anchor, refs) {
229
- if (1 === refs.length)
248
+ if (1 === refs.length) {
230
249
  return refs[0];
250
+ }
231
251
  const { nth, of } = { ...anchor };
232
252
  return 'number' === typeof nth && of === refs.length ? refs[nth] : undefined;
233
253
  }
@@ -246,8 +266,9 @@ export function refFor(anchor, refs) {
246
266
  */
247
267
  export function reresolved(anchor, refs) {
248
268
  const drift = ambiguityDrift(anchor, refs);
249
- if (drift !== null)
269
+ if (drift !== null) {
250
270
  return { drift };
271
+ }
251
272
  const ref = refFor(anchor, refs);
252
273
  return ref === undefined ? undefined : { ref };
253
274
  }
@@ -268,8 +289,9 @@ export function expectElementDrift(value, hint) {
268
289
  anchor: value,
269
290
  nearest: nearestTestid(value, present),
270
291
  };
271
- if (nearestIsAmbiguous(value, present))
292
+ if (nearestIsAmbiguous(value, present)) {
272
293
  drift.ambiguous = true;
294
+ }
273
295
  return drift;
274
296
  }
275
297
  /**
@@ -296,8 +318,9 @@ async function settleTick(session, sleep) {
296
318
  await new Promise((resolve) => {
297
319
  let done = false;
298
320
  const finish = () => {
299
- if (done)
321
+ if (done) {
300
322
  return;
323
+ }
301
324
  done = true;
302
325
  resolve();
303
326
  };
@@ -313,15 +336,34 @@ async function settleTick(session, sleep) {
313
336
  export async function queryOnce(session, queryArgs) {
314
337
  return readQuery(await session.command(ReticleCommand.QUERY, queryArgs));
315
338
  }
316
- export async function resolveQuery(session, queryArgs, sleep, now = Date.now) {
317
- let last = readQuery(await session.command(ReticleCommand.QUERY, queryArgs));
339
+ export async function resolveQuery(session, queryArgs, sleep, now = Date.now, read = readQuery) {
340
+ return await resolveAnyQuery(session, [queryArgs], sleep, now, read);
341
+ }
342
+ /**
343
+ * Several queries for one control, pooled: each attempt runs every query and joins the refs, so
344
+ * the settle budget is spent once, not once for each query. A role anchor with `alsoRoles` uses it.
345
+ */
346
+ export async function resolveAnyQuery(session, queries, sleep, now = Date.now, read = readQuery) {
347
+ const look = async () => {
348
+ const reads = [];
349
+ for (const queryArgs of queries) {
350
+ reads.push(read(await session.command(ReticleCommand.QUERY, queryArgs)));
351
+ }
352
+ const hint = reads.find((read) => read.hint !== undefined)?.hint;
353
+ return { refs: reads.flatMap((read) => read.refs), ...(hint === undefined ? {} : { hint }) };
354
+ };
355
+ let last = await look();
318
356
  const deadline = now() + ANCHOR_SETTLE_BUDGET_MS;
319
357
  for (let attempt = 1; 0 === last.refs.length && now() < deadline && attempt < ANCHOR_SETTLE_MAX_ATTEMPTS; attempt += 1) {
320
358
  await settleTick(session, sleep);
321
- last = readQuery(await session.command(ReticleCommand.QUERY, queryArgs));
359
+ last = await look();
322
360
  }
323
361
  return last;
324
362
  }
363
+ /** `resolveQuery`'s reader for a nameless role anchor: only the matches with no accessible name. */
364
+ export function readNamelessQuery(result) {
365
+ return { refs: namelessRefs(result) };
366
+ }
325
367
  /** Re-resolve a testid anchor. */
326
368
  export function resolveTestid(session, value, sleep) {
327
369
  return resolveQuery(session, { by: QueryBy.TESTID, value }, sleep);
@@ -337,20 +379,83 @@ export function componentLabel(anchor) {
337
379
  }
338
380
  /** The value of a step's primary anchor, for labelling the result row. */
339
381
  export function anchorLabel(anchor) {
340
- if (anchor.kind === AnchorKind.TESTID)
382
+ if (anchor.kind === AnchorKind.TESTID) {
341
383
  return anchor.value;
342
- if (anchor.kind === AnchorKind.SIGNAL)
384
+ }
385
+ if (anchor.kind === AnchorKind.SIGNAL) {
343
386
  return anchor.name;
344
- if (anchor.kind === AnchorKind.COMPONENT)
387
+ }
388
+ if (anchor.kind === AnchorKind.COMPONENT) {
345
389
  return componentLabel(anchor);
390
+ }
346
391
  return anchor.name ?? anchor.role;
347
392
  }
348
393
  /** QUERY args for a component auto-anchor — source (precise) + component name (coarse) as given. */
349
394
  export function componentQueryArgs(anchor) {
350
395
  const args = { by: QueryBy.COMPONENT };
351
- if (anchor.component !== undefined)
396
+ if (anchor.component !== undefined) {
352
397
  args['component'] = anchor.component;
353
- if (anchor.source !== undefined)
398
+ }
399
+ if (anchor.source !== undefined) {
354
400
  args['source'] = anchor.source;
401
+ }
402
+ if (anchor.text !== undefined) {
403
+ args['text'] = anchor.text;
404
+ }
405
+ // An older SDK ignores a role on a component query and answers as before.
406
+ if (anchor.role !== undefined) {
407
+ args['role'] = anchor.role;
408
+ }
355
409
  return args;
356
410
  }
411
+ /** Query args for a NAMED role anchor — the handle that identifies an instance, not a JSX site. */
412
+ export function roleQueryArgs(anchor) {
413
+ return {
414
+ by: QueryBy.ROLE,
415
+ value: anchor.role,
416
+ ...(anchor.name === undefined ? {} : { name: anchor.name }),
417
+ };
418
+ }
419
+ /** One query for each role the anchor accepts: its own role, then `alsoRoles`. */
420
+ function roleQueries(anchor) {
421
+ return [anchor.role, ...(anchor.alsoRoles ?? [])].map((role) => roleQueryArgs({ ...anchor, role }));
422
+ }
423
+ /**
424
+ * Resolve a role anchor. ONE function for the first look and the re-resolve after a stale ref: the
425
+ * re-resolve used to count every control of the role, so a nameless anchor's position came back
426
+ * ambiguous on a working page.
427
+ *
428
+ * A nameless anchor's position counts only the nameless controls of its role (an icon-only edit
429
+ * button is the 2nd nameless button of its row, not the 5th button), so it reads only those.
430
+ */
431
+ export async function resolveRoleAnchor(session, anchor, sleep) {
432
+ // Every role the anchor accepts (`alsoRoles`), pooled in one settle loop; a nameless anchor keeps
433
+ // only the matches with no accessible name.
434
+ const found = await resolveAnyQuery(session, roleQueries(anchor), sleep, Date.now, anchor.name === undefined ? readNamelessQuery : undefined);
435
+ if (found.refs.length > 0 || anchor.name === undefined || anchor.within === undefined) {
436
+ return found;
437
+ }
438
+ const refs = await refsWithin(session, anchor.role, anchor.within);
439
+ return refs.length > 0 ? { refs } : found;
440
+ }
441
+ /**
442
+ * The controls of `role` inside the recorded container chain, for a named control whose name is
443
+ * gone (a correct build may word a button differently). The chain is walked outermost first, and
444
+ * each container must be the only one of its role in the one before. When a container is not
445
+ * unique, its matches come back, so the caller reports the step as ambiguous and does not act.
446
+ */
447
+ async function refsWithin(session, role, within) {
448
+ let scope;
449
+ for (const container of [...within].reverse()) {
450
+ const { refs } = await queryOnce(session, {
451
+ by: QueryBy.ROLE,
452
+ value: container,
453
+ ...(scope === undefined ? {} : { scope }),
454
+ });
455
+ if (1 !== refs.length) {
456
+ return refs;
457
+ }
458
+ scope = refs[0];
459
+ }
460
+ return (await queryOnce(session, { by: QueryBy.ROLE, value: role, scope })).refs;
461
+ }
@@ -19,6 +19,7 @@
19
19
  * Pure: no IO, no clock.
20
20
  */
21
21
  import type { FlowFile, FlowStep } from '@reticlehq/core';
22
+ import { z } from 'zod';
22
23
  export declare const FlowAssertionGrade: {
23
24
  /** At least one step (or the success end-condition) asserts a signal/network consequence. */
24
25
  readonly ASSERTED: "asserted";
@@ -45,9 +46,28 @@ export interface FlowAssertionClassification {
45
46
  * intent but only presence-only checks is the dangerous case — it claims a goal it cannot verify.
46
47
  */
47
48
  intentVerified: boolean;
49
+ /**
50
+ * How many checks of each predicate kind the flow runs, over every step, sub-step and the success
51
+ * state, composites flattened. Absent when the flow checks nothing. A presence-only grade with
52
+ * this list says WHAT is checked, so the grade does not read as "checks nothing" (#1840).
53
+ */
54
+ checks?: Record<string, number>;
48
55
  /** Present for presence-only / assertion-free flows: how to make the flow a real test. */
49
56
  warning?: string;
50
57
  }
58
+ /**
59
+ * The `assertions` block of the `reticle_flow_save` result. Declared here, next to the type it
60
+ * describes, because a validating profile removes a field that the output schema does not declare.
61
+ */
62
+ export declare const FLOW_ASSERTIONS_OUTPUT: z.ZodOptional<z.ZodObject<{
63
+ grade: z.ZodString;
64
+ hasConsequenceAssertion: z.ZodBoolean;
65
+ totalSteps: z.ZodNumber;
66
+ consequenceSteps: z.ZodNumber;
67
+ weakSteps: z.ZodNumber;
68
+ checks: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
69
+ warning: z.ZodOptional<z.ZodString>;
70
+ }, z.core.$strip>>;
51
71
  /**
52
72
  * A step expect counts the same as a flow-level one, because replay now evaluates the same set.
53
73
  *