@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
@@ -43,17 +43,53 @@ the first flow. It never drives: the Harness is opt-in and runs at any instrumen
43
43
  const VERIFY_DETAIL = `Drives the URL and verifies the saved flows. Exit 0 means every one passed.
44
44
 
45
45
  --explore [--persona <who>] no saved flows? let Reticle drive the app and record them
46
+ --against <reference-url> compare the URL with a reference build: drive both with the same
47
+ actions, and report each difference in what users see (exit 1)
46
48
  --select <label> repeatable: verify only flows carrying these labels
47
49
  --results-json <file> also write one verdict per journey as JSON, for CI
48
50
  --confirm-dangerous this run may press what the replay guard holds as destructive
51
+ --require-start-control fail a flow whose first control is not on its freshly loaded start page
52
+ --sweep judge every step, also after one fails (the results score counts them all)
49
53
  --storage-state <file> start signed in
50
- --session-id <id>, --timeout N, --headed, --port N
54
+ --session-id <id>, --timeout <ms>, --headed, --port N
51
55
  --expect '<json predicate>' | --expect-file <path>
52
56
  one verdict, no saved flows needed. Asks a running daemon,
53
57
  never starts one, and waits up to --timeout ms (default 4000)
54
58
  for it to come true. Exit 0 only on verified:"yes". For an
55
59
  action --expect cannot do, use the HTTP MCP transport:
56
60
  https://docs.reticle.sh/http-transport.md`;
61
+ const GRADE_DETAIL = `Grades a finished run of an app from snapshots of its stored data. It needs no
62
+ daemon and no browser.
63
+
64
+ grade snapshot --state <file> --out <file> [--url <url>]
65
+ read the declared state sources (and the page the run ended on) into a snapshot
66
+ grade judge --reference <file> --again <file> --candidate <file>
67
+ [--expect <file>] [--answer <text>] [--json]
68
+ compare the candidate snapshot with two snapshots of the correct run. A line that
69
+ differs between the two correct runs is ignored. --expect declares a source whose
70
+ rows are the correct answer, read now. Exit 0 = pass, 1 = fail, 2 = unverified
71
+ grade tables --list <command> --rows <template> [--container <name>] [--skip <prefix,...>]
72
+ --out <file>
73
+ write a declaration with one source for each table; {table} in the template is
74
+ the table name; --skip leaves out the tables whose name starts with a prefix
75
+ grade suggest --state <file> --reference <file> --again <file> --out <file>
76
+ keep only the sources that read the same on two correct runs`;
77
+ const COMPARE_DETAIL = `Drives two running builds of one app the same way, for example main and a pull request
78
+ preview, and reports each difference in what a person sees and in the requests and console
79
+ errors the app made. Install nothing in the app.
80
+
81
+ It plans every action on every control of the reference, drives the plan on the reference two
82
+ times and on the candidate one time, and ignores what changes between the two reference runs.
83
+ Point it at builds whose data you can change: it presses every button it finds.
84
+
85
+ --base <url> the build before the change: tag each difference as a regression, a missed
86
+ target or a change, and print a score
87
+ --state <file> a JSON file { "sources": [...] } that declares how to read the app's stored
88
+ data (kind command, http or file). Compare reads it after each sequence on
89
+ every build, so a change to a record that no page shows is a difference.
90
+ Start each row with the record id, so a changed row names its record
91
+ --json print one JSON object
92
+ Exit 0 = same, 1 = different, 2 = the run proves nothing (a lost browser session, or an error).`;
57
93
  const BROWSER_NOTE = `The browser Reticle opens for your agent is shown so you can watch the run. --headless hides
58
94
  it; CI, RETICLE_HEADLESS=1, or a Linux machine with no display hides it too.`;
59
95
  /** What `--http` starts. MCP is already on the bridge port; this flag is the verify endpoint. */
@@ -92,26 +128,43 @@ const COMMANDS = [
92
128
  {
93
129
  name: 'verify',
94
130
  synopsis: 'verify <url> [--explore] [--expect <json>]',
95
- summary: 'Check the saved flows against the running app; exit 0 means pass',
131
+ summary: 'Check saved flows, or one claim with --expect, against the running app; exit 0 = pass',
96
132
  group: HelpGroup.CHECK,
97
133
  detail: VERIFY_DETAIL,
98
134
  },
135
+ {
136
+ name: 'compare',
137
+ synopsis: 'compare <reference-url> <candidate-url> [--base <url>] [--state <file>] [--json] [--report <file>]',
138
+ summary: 'Drive two builds the same way and list what users would see change',
139
+ group: HelpGroup.CHECK,
140
+ detail: COMPARE_DETAIL,
141
+ },
142
+ {
143
+ name: 'grade',
144
+ synopsis: 'grade <snapshot|judge|tables|suggest> [flags]',
145
+ summary: "Grade a finished run from snapshots of the app's stored data",
146
+ group: HelpGroup.MORE,
147
+ detail: GRADE_DETAIL,
148
+ },
99
149
  {
100
150
  name: 'gate',
101
- synopsis: 'gate [--since <ref>] [--accept-coverage] [--hook] [file...]',
151
+ synopsis: 'gate [--list] [--since <ref>] [--accept-coverage] [--hook] [file...]',
102
152
  summary: 'Fail unless passing checks cover the flows your changes touch',
103
153
  group: HelpGroup.CHECK,
154
+ detail: '--list names the saved flows your changes touch, and checks nothing.',
104
155
  },
105
156
  {
106
157
  name: 'status',
107
158
  synopsis: 'status [--json] [--port N]',
108
159
  summary: 'Is Reticle running, is your app connected, are you signed in',
160
+ detail: 'Shows the enterprise license when a key is set. --json always carries it.',
109
161
  group: HelpGroup.MACHINE,
110
162
  },
111
163
  {
112
164
  name: 'doctor',
113
- synopsis: 'doctor [--port N]',
165
+ synopsis: 'doctor [--fix] [--port N]',
114
166
  summary: 'Find what is wrong with the setup, and what is missing from coverage',
167
+ detail: '--fix repairs what Reticle can repair safely, and names the step for the rest.',
115
168
  group: HelpGroup.MACHINE,
116
169
  },
117
170
  {
@@ -131,17 +184,12 @@ const COMMANDS = [
131
184
  "With --force, frees the port by the Reticle daemon LISTENING on it, never the agent's MCP\n" +
132
185
  'proxy, and refuses a listener that is not Reticle.',
133
186
  },
134
- {
135
- name: 'restart',
136
- synopsis: 'restart [--force] [--port N]',
137
- summary: 'Stop Reticle, then start it and wait until it is listening',
138
- group: HelpGroup.MACHINE,
139
- },
140
187
  {
141
188
  name: 'update',
142
- synopsis: 'update',
189
+ synopsis: 'update [--rollback]',
143
190
  summary: 'Install the latest version and restart',
144
191
  group: HelpGroup.MACHINE,
192
+ detail: '--rollback restores the previous version and restarts.',
145
193
  },
146
194
  {
147
195
  name: 'logout',
@@ -169,7 +217,7 @@ const COMMANDS = [
169
217
  synopsis: 'sync [--watch] [--move-history]',
170
218
  summary: 'Send local runs to the dashboard and collect decisions',
171
219
  group: HelpGroup.MORE,
172
- detail: '`reticle push` is the same command.\n\n --watch keep sending as runs are written\n' +
220
+ detail: ' --watch keep sending as runs are written\n' +
173
221
  ' --move-history also send runs, flows and records made under the previous link',
174
222
  },
175
223
  {
@@ -190,12 +238,6 @@ const COMMANDS = [
190
238
  summary: 'What this project sends to the dashboard',
191
239
  group: HelpGroup.MORE,
192
240
  },
193
- {
194
- name: 'affected',
195
- synopsis: 'affected [--since <ref>] [file...]',
196
- summary: 'Which saved flows must re-verify for the changed files',
197
- group: HelpGroup.MORE,
198
- },
199
241
  {
200
242
  name: 'report',
201
243
  synopsis: 'report [--session <id>] [--hook]',
@@ -235,7 +277,7 @@ const COMMANDS = [
235
277
  synopsis: 'serve [--port N] [--drive <url>] [--headless] [--headed] [--http] [--http-port N] [--http-token T]',
236
278
  summary: 'Start the daemon in the background; returns once it answers',
237
279
  group: HelpGroup.MORE,
238
- detail: DAEMON_DETAIL,
280
+ detail: `--restart [--force] stops Reticle, then starts it and waits until it listens.\n\n${DAEMON_DETAIL}`,
239
281
  },
240
282
  {
241
283
  name: 'drive',
@@ -249,18 +291,6 @@ const COMMANDS = [
249
291
  summary: 'Register the MCP server with the coding agents on this machine',
250
292
  group: HelpGroup.MORE,
251
293
  },
252
- {
253
- name: 'license',
254
- synopsis: 'license',
255
- summary: 'Show the enterprise license status',
256
- group: HelpGroup.MORE,
257
- },
258
- {
259
- name: 'rollback',
260
- synopsis: 'rollback',
261
- summary: 'Restore the previous version and restart',
262
- group: HelpGroup.MORE,
263
- },
264
294
  {
265
295
  name: 'version',
266
296
  synopsis: 'version',
@@ -39,6 +39,11 @@ export interface VerifyConnection {
39
39
  * only the connection can read one.
40
40
  */
41
41
  listFlows(select?: readonly string[]): Promise<string[]>;
42
+ /**
43
+ * Each named flow's saved step count, for the results score: without it a replay that stopped at
44
+ * step 2 of 10 reads as a 2-step journey. Optional; a name it cannot read is left out.
45
+ */
46
+ plannedSteps?(names: readonly string[]): Promise<ReadonlyMap<string, number>>;
42
47
  /**
43
48
  * Drive the app with a model and save what it drove. Present only when a model is configured.
44
49
  *
@@ -190,8 +195,8 @@ export declare function routeVerify(args: {
190
195
  hasPredicate: boolean;
191
196
  /**
192
197
  * True when the caller asked for something only a browser of OUR OWN can do: `--headed` or
193
- * `--storage-state`. The running daemon's tab can honour neither, so replaying there would
194
- * silently ignore the request.
198
+ * `--storage-state`, or a rule its replay tool does not take (`--require-start-control`). The
199
+ * running daemon's tab can honour none of them, so replaying there would silently ignore the request.
195
200
  */
196
201
  wantsOwnBrowser?: boolean;
197
202
  /**
@@ -230,5 +235,14 @@ export declare function handleVerify(parsed: {
230
235
  resultsJson?: string;
231
236
  /** Acknowledge the replay guard's destructive steps ("Place order") for this run only. */
232
237
  confirmDangerous?: boolean;
233
- }): void;
238
+ /** Fail a flow whose step 1 control is missing on its freshly loaded start page. */
239
+ requireStartControl?: boolean;
240
+ /** Judge every step, also after one fails. */
241
+ sweep?: boolean;
242
+ },
243
+ /**
244
+ * Called once, after the last verdict line and before the exit. `cli.ts` passes the CI line:
245
+ * `telemetry` already reaches this directory, so this directory does not reach back.
246
+ */
247
+ beforeExit?: () => void): void;
234
248
  export {};
@@ -12,6 +12,7 @@
12
12
  import { cliRunAuthor } from '../../judgement/runs/verification-sync.js';
13
13
  import { join, basename } from 'node:path';
14
14
  import { readFileSync } from 'node:fs';
15
+ import { noSessionMessage } from './verify-no-session.js';
15
16
  import { runAdhocVerdict } from './adhoc-verdict.js';
16
17
  import { callerArtifactRoot } from '../../memory/project/link-directory.js';
17
18
  import { verifyResults } from './verify-results.js';
@@ -59,9 +60,6 @@ const DEFAULT_PROJECT_NAME = 'app';
59
60
  export function verifyProjectName(linked, configured, folder) {
60
61
  return linked ?? configured ?? (folder || DEFAULT_PROJECT_NAME);
61
62
  }
62
- const MSG_NO_SESSION = 'No app connected: Reticle drove the URL but no @reticlehq/browser session dialed back.\n' +
63
- ' Make sure the SDK is in the build and reticle.connect() runs on the preview page' +
64
- ' (for a non-localhost preview: allowNonLocalhost + a pairing token).';
65
63
  const MSG_NO_FLOWS = 'No saved flows to verify (.reticle/flows is empty), so refusing to report a pass for verifying nothing.\n' +
66
64
  ' Flows are recorded interactively by an agent (reticle_record{action:"start"} → act → reticle_flow_save via the\n' +
67
65
  ' MCP tools), then committed to .reticle/flows/. In CI, check those files in and re-run `reticle verify`.\n' +
@@ -86,8 +84,9 @@ async function openConnection(ports) {
86
84
  /** Orchestration: boot → wait for a session → replay flows → verdict → exit code. Browser-free. */
87
85
  export async function runVerify(args, outer) {
88
86
  const conn = await openConnection(outer);
89
- if (conn === undefined)
87
+ if (conn === undefined) {
90
88
  return;
89
+ }
91
90
  /*
92
91
  * The exit is held until the connection has closed. The live exit is process.exit, and close()
93
92
  * runs the last cloud sync cycle that sends this run's flows and screenshots: exiting first skipped
@@ -103,7 +102,7 @@ export async function runVerify(args, outer) {
103
102
  try {
104
103
  const ready = await conn.sessionReady(args.timeoutMs);
105
104
  if (!ready) {
106
- ports.fail(MSG_NO_SESSION);
105
+ ports.fail(noSessionMessage(args.timeoutMs));
107
106
  ports.exit(EXIT_FAIL);
108
107
  return;
109
108
  }
@@ -132,8 +131,9 @@ export async function runVerify(args, outer) {
132
131
  */
133
132
  if (0 === names.length && (true === args.explore || args.persona !== undefined)) {
134
133
  const explored = await explore(conn, args, ports);
135
- if (!explored)
134
+ if (!explored) {
136
135
  return;
136
+ }
137
137
  names = await conn.listFlows();
138
138
  }
139
139
  if (0 === names.length) {
@@ -180,7 +180,7 @@ export async function runVerify(args, outer) {
180
180
  // The run's OWN injected timestamp — no wall clock is read in this path (rule 7).
181
181
  [{ phase: VerifyPhase.PUSHED, at: run.createdAt }], cloud, cloudFetch).catch(() => undefined);
182
182
  if (args.resultsJson !== undefined && ports.writeResults !== undefined) {
183
- await ports.writeResults(args.resultsJson, `${JSON.stringify(verifyResults(run, pushed), null, 2)}\n`);
183
+ await ports.writeResults(args.resultsJson, `${JSON.stringify(verifyResults(run, pushed, (await conn.plannedSteps?.(run.flows.map((f) => f.name))) ?? new Map()), null, 2)}\n`);
184
184
  }
185
185
  ports.out(renderRunReport(run));
186
186
  ports.exit(run.verdict.status === VerdictStatus.PASS ? EXIT_PASS : EXIT_FAIL);
@@ -192,8 +192,9 @@ export async function runVerify(args, outer) {
192
192
  finally {
193
193
  await conn.close().catch(() => undefined);
194
194
  // In the finally, so the early refusals that `return` from the try exit too.
195
- if (exitCode !== undefined)
195
+ if (exitCode !== undefined) {
196
196
  outer.exit(exitCode);
197
+ }
197
198
  }
198
199
  }
199
200
  /**
@@ -269,8 +270,9 @@ function delay(ms) {
269
270
  async function waitForSession(sessions, timeoutMs, now) {
270
271
  const deadline = now() + timeoutMs;
271
272
  while (now() < deadline) {
272
- if (sessions.count() > 0)
273
+ if (sessions.count() > 0) {
273
274
  return true;
275
+ }
274
276
  await delay(SESSION_POLL_MS);
275
277
  }
276
278
  return sessions.count() > 0;
@@ -298,8 +300,9 @@ export function buildVerifyDeps(running, reticleRoot, now) {
298
300
  // RETICLE_API_KEY was told to run `reticle connect` when asking the Harness to drive.
299
301
  linkedCloud: linkedCloudPort(fs, reticleRoot, homedir(), process.env),
300
302
  };
301
- if (running.realInput !== undefined)
303
+ if (running.realInput !== undefined) {
302
304
  deps.realInput = running.realInput;
305
+ }
303
306
  return deps;
304
307
  }
305
308
  /** Split a drive URL into its origin + whether it's loopback — decides token/injection pairing. */
@@ -347,9 +350,24 @@ async function openLiveConnection(opts) {
347
350
  });
348
351
  const deps = buildVerifyDeps(running, opts.reticleRoot, opts.now);
349
352
  const harnessEnv = await withLinkedCredential(deps, process.env);
350
- const runner = new ReticleRunner(createRunnerPort(deps, opts.sessionId, { confirmDangerous: true === opts.confirmDangerous }));
353
+ const runner = new ReticleRunner(createRunnerPort(deps, opts.sessionId, {
354
+ confirmDangerous: true === opts.confirmDangerous,
355
+ requireStartControl: true === opts.requireStartControl,
356
+ sweep: true === opts.sweep,
357
+ }));
351
358
  return {
352
359
  sessionReady: (timeoutMs) => waitForSession(deps.sessions, timeoutMs, opts.now),
360
+ plannedSteps: async (names) => {
361
+ const planned = new Map();
362
+ for (const name of names) {
363
+ const loaded = await deps.flows.load(name).catch(() => undefined);
364
+ if (true === loaded?.ok) {
365
+ // A sequence counts its sub-steps, as the score does.
366
+ planned.set(name, loaded.value.steps.reduce((n, step) => n + (step.steps?.length ?? 1), 0));
367
+ }
368
+ }
369
+ return planned;
370
+ },
353
371
  listFlows: async (select) => select === undefined || 0 === select.length
354
372
  ? deps.flows.list()
355
373
  : (await resolveSuiteSelection(deps, projectOnly(undefined), { labels: [...select] })).run,
@@ -471,7 +489,7 @@ export function expectNeedsDaemonMessage(port, presence) {
471
489
  export function daemonLacksKeyMessage(port) {
472
490
  return (`${ReticleEnv.API_KEY} is set in this shell, but the daemon on port ${String(port)} was ` +
473
491
  'started without it, and the daemon is what asks the platform to drive. Restart it from this ' +
474
- `shell so it inherits the key: npx @reticlehq/server restart --port ${String(port)}`);
492
+ `shell so it inherits the key: npx @reticlehq/server serve --restart --port ${String(port)}`);
475
493
  }
476
494
  /** What `verify` does next, once the options and the state of the port are both known. */
477
495
  export const VerifyRoute = {
@@ -501,8 +519,9 @@ export function routeVerify(args) {
501
519
  ? { route: VerifyRoute.ADHOC }
502
520
  : { route: VerifyRoute.REFUSE, message: expectNeedsDaemonMessage(args.port, args.presence) };
503
521
  }
504
- if (args.presence !== PortPresence.DAEMON)
522
+ if (args.presence !== PortPresence.DAEMON) {
505
523
  return { route: VerifyRoute.FLOWS };
524
+ }
506
525
  if (true === args.explore && true !== args.wantsOwnBrowser && true !== args.hasSavedFlows) {
507
526
  return { route: VerifyRoute.ADHOC_EXPLORE };
508
527
  }
@@ -512,7 +531,12 @@ export function routeVerify(args) {
512
531
  ? { route: VerifyRoute.REFUSE, message: portBusyMessage(args.port) }
513
532
  : { route: VerifyRoute.ADHOC_SUITE };
514
533
  }
515
- export function handleVerify(parsed) {
534
+ export function handleVerify(parsed,
535
+ /**
536
+ * Called once, after the last verdict line and before the exit. `cli.ts` passes the CI line:
537
+ * `telemetry` already reaches this directory, so this directory does not reach back.
538
+ */
539
+ beforeExit = () => undefined) {
516
540
  const now = () => Date.now();
517
541
  /*
518
542
  * `--expect-file` is `--expect` with the shell taken out of the loop.
@@ -556,10 +580,15 @@ export function handleVerify(parsed) {
556
580
  ...(parsed.storageState !== undefined ? { storageState: parsed.storageState } : {}),
557
581
  ...(parsed.sessionId !== undefined ? { sessionId: parsed.sessionId } : {}),
558
582
  ...(true === parsed.confirmDangerous ? { confirmDangerous: true } : {}),
583
+ ...(true === parsed.requireStartControl ? { requireStartControl: true } : {}),
584
+ ...(true === parsed.sweep ? { sweep: true } : {}),
559
585
  }),
560
586
  out: (line) => process.stdout.write(`${line}\n`),
561
587
  fail: (line) => process.stderr.write(`${line}\n`),
562
- exit: (code) => process.exit(code),
588
+ exit: (code) => {
589
+ beforeExit();
590
+ process.exit(code);
591
+ },
563
592
  cloud: linkedRunsCloudPort(createNodeFileSystem(), reticleRoot, homedir(), process.env),
564
593
  dashboardUrl: () => readDashboardUrl(reticleRoot),
565
594
  writeResults: (path, text) => writeFile(path, text, 'utf8'),
@@ -569,8 +598,9 @@ export function handleVerify(parsed) {
569
598
  // this used to reach the user as a raw node stack rather than as an answer.
570
599
  void (async () => {
571
600
  const keyNote = (await resolveProjectCloud(createNodeFileSystem(), reticleRoot, homedir(), process.env)).note;
572
- if (keyNote !== undefined)
601
+ if (keyNote !== undefined) {
573
602
  process.stderr.write(`${keyNote}\n`);
603
+ }
574
604
  const port = parsed.port ?? RETICLE_DEFAULT_PORT;
575
605
  // A lease opened for this verdict writes its runs into this folder's project, not `unmatched/`.
576
606
  const root = callerArtifactRoot(process.cwd());
@@ -578,7 +608,11 @@ export function handleVerify(parsed) {
578
608
  const presence = await probePresence(port, { tcpOpen: probeDaemon, status: fetchStatus });
579
609
  const plan = routeVerify({
580
610
  hasPredicate: expectation !== undefined,
581
- wantsOwnBrowser: !parsed.headless || parsed.storageState !== undefined,
611
+ // `--require-start-control` too: the daemon's replay tool does not take that rule.
612
+ wantsOwnBrowser: !parsed.headless ||
613
+ parsed.storageState !== undefined ||
614
+ true === parsed.requireStartControl ||
615
+ true === parsed.sweep,
582
616
  explore: true === parsed.explore || parsed.persona !== undefined,
583
617
  hasSavedFlows: 0 <
584
618
  (await new FlowStore(createNodeFileSystem(), reticleRoot, { now: Date.now }).list()).length,
@@ -613,8 +647,9 @@ export function handleVerify(parsed) {
613
647
  ...(parsed.sessionId === undefined ? {} : { sessionId: parsed.sessionId }),
614
648
  ...((t) => (t === undefined || 0 === t.length ? {} : { token: t }))(readOrCreatePairingTokenSync(defaultPairingTokenDir())),
615
649
  });
616
- for (const line of verdict.lines)
650
+ for (const line of verdict.lines) {
617
651
  ports.out(line);
652
+ }
618
653
  ports.exit(verdict.code);
619
654
  return;
620
655
  }
@@ -631,8 +666,9 @@ export function handleVerify(parsed) {
631
666
  ...(parsed.sessionId === undefined ? {} : { sessionId: parsed.sessionId }),
632
667
  ...((t) => (t === undefined || 0 === t.length ? {} : { token: t }))(readOrCreatePairingTokenSync(defaultPairingTokenDir())),
633
668
  });
634
- for (const line of suite.lines)
669
+ for (const line of suite.lines) {
635
670
  ports.out(line);
671
+ }
636
672
  ports.exit(suite.code);
637
673
  return;
638
674
  }
@@ -647,15 +683,17 @@ export function handleVerify(parsed) {
647
683
  ...callerRoot,
648
684
  ...((t) => (t === undefined || 0 === t.length ? {} : { token: t }))(readOrCreatePairingTokenSync(defaultPairingTokenDir())),
649
685
  });
650
- for (const line of explored.lines)
686
+ for (const line of explored.lines) {
651
687
  ports.out(line);
688
+ }
652
689
  // The drive runs INSIDE the daemon, so the key has to be in ITS environment. A reader who
653
690
  // exported it in this shell and was told to link has been told to do the thing they just
654
691
  // did; say where the key is actually missing.
655
692
  if (0 !== explored.code && harnessAvailable(process.env)) {
656
693
  const refusedForKey = explored.lines.some((line) => line.includes(MSG_NO_HARNESS_KEY));
657
- if (refusedForKey)
694
+ if (refusedForKey) {
658
695
  ports.fail(daemonLacksKeyMessage(port));
696
+ }
659
697
  }
660
698
  ports.exit(explored.code);
661
699
  return;
@@ -5,7 +5,6 @@
5
5
  * `credentials.json` (per-project api keys from `reticle link`). The non-secret repo binding + sync policy
6
6
  * is `<repo>/.reticle/cloud.json`. Auth for a command = `RETICLE_API_KEY` env (agent) OR the login token.
7
7
  */
8
- import { CreatedProjectSchema } from './project-name.js';
9
8
  import { keyRefusalAtLastSync } from '../../memory/project/sync-status.js';
10
9
  import { ReticleEnv, apiKeyFrom } from '@reticlehq/core';
11
10
  import { writeHarnessSwitch } from '../../memory/cloud/harness-switch.js';
@@ -24,11 +23,10 @@ import { RETICLE_CONFIG_BASENAME } from './ports/resolve/cli-port.js';
24
23
  import { cmdLink, validateKey } from './cloud-link.js';
25
24
  // `link` moved to its own module; the CLI entry still imports it from here.
26
25
  export { cmdLink } from './cloud-link.js';
27
- import { normalizeUrl } from './auth/cloud-session.js';
28
26
  import { chooseWorkspace, cmdLogin, cmdLogout } from './cloud-login.js';
29
27
  import { cmdTry } from './try-command.js';
30
28
  import { CLOUD_COMMANDS } from './cli-parse-grammar.js';
31
- import { api, baseUrl, bearer, emit, err, flags, hint, readJson, readSession, readSessionFor, RETICLE_DIR, } from './cloud-kit.js';
29
+ import { api, baseUrl, emit, err, flags, hint, readJson, readSession, readSessionFor, RETICLE_DIR, } from './cloud-kit.js';
32
30
  import { syncRequest } from '../../memory/cloud/cloud-sync.js';
33
31
  import { GateExit, gateExitWith } from './answers/gate-exit.js';
34
32
  import { brokenOf } from './known-regression.js';
@@ -36,7 +34,7 @@ import { describeUnsyncedBriefly } from '../../memory/cloud/unsynced-roots.js';
36
34
  import { machineUnsyncedRoots } from '../../memory/project/sync-status.js';
37
35
  import { runSyncCycle } from '../../memory/cloud/sync-cycle.js';
38
36
  import { describeSync } from '../../memory/cloud/sync-describe.js';
39
- import { diskSink, diskSource, policyScreenshots, readCloudIssues, readCloudState, } from '../../memory/cloud/sync-disk.js';
37
+ import { diskSink, diskSource, policyScreenshots, readCloudState, } from '../../memory/cloud/sync-disk.js';
40
38
  import { fetchPlatformRun } from '../../features/harness/platform/platform-drives.js';
41
39
  import { SERVER_VERSION } from '../version/identity/server-version.js';
42
40
  /**
@@ -82,48 +80,6 @@ const linkedRoot = () => {
82
80
  const onlyHere = !existsSync(join(project, CLOUD_LINK_FILE)) && existsSync(join(here, CLOUD_LINK_FILE));
83
81
  return onlyHere ? here : project;
84
82
  };
85
- /**
86
- * `reticle whoami` — the one call an agent (or a confused human) makes to know its state: who am I logged
87
- * in as, and is THIS repo attached to a cloud project (and with what sync policy / verify mode)?
88
- */
89
- const cmdWhoami = async () => {
90
- const session = await readSession();
91
- const fs = createNodeFileSystem();
92
- const cloud = await resolveProjectCloud(fs, linkedRoot(), homedir(), process.env);
93
- /*
94
- * The sync half of "what is my state". Without it the honest answer to "why does the dashboard
95
- * look old?" was to go and read a JSON file — and the two failure modes a person actually hits
96
- * (nothing has synced yet, and the last attempt errored) looked identical from out here.
97
- */
98
- const reticleRoot = linkedRoot();
99
- const state = readCloudState(reticleRoot);
100
- const decisions = Object.keys(readCloudIssues(reticleRoot).triage).length;
101
- emit({
102
- loggedInAs: session?.orgName ?? null,
103
- sync: {
104
- lastPushAt: state.lastPushAt ?? null,
105
- lastPullAt: state.lastPullAt ?? null,
106
- /** Present only when the machine is behind BECAUSE something failed, which is the useful case. */
107
- ...(state.lastError === undefined ? {} : { lastError: state.lastError }),
108
- /** Decisions collected from the dashboard and readable locally. */
109
- decisionsHeld: decisions,
110
- neverSynced: state.lastPullAt === undefined,
111
- },
112
- repo: {
113
- attached: cloud.config !== null,
114
- projectId: cloud.projectId,
115
- url: cloud.config?.url ?? null,
116
- sync: cloud.policy,
117
- verify: cloud.verify,
118
- },
119
- });
120
- // Signed out, `link` refuses with "run reticle login first"; `connect` does both in one go.
121
- if (null === cloud.config)
122
- hint(null === session
123
- ? 'this repo is not attached — run `reticle connect` (signs in and links in one step)'
124
- : 'this repo is not attached — run `reticle connect`');
125
- return 0;
126
- };
127
83
  /**
128
84
  * Who is signed in and which project this repo is linked to, for `reticle status`. Local files
129
85
  * only: status must answer with no network.
@@ -146,58 +102,6 @@ export const accountStatusFields = async () => {
146
102
  ...(refused === undefined ? {} : { keyRefused: refused }),
147
103
  };
148
104
  };
149
- /** `reticle project ls` / `reticle project create <name>` — key- or session-authed. */
150
- const cmdProject = async (argv) => {
151
- const active = await readSession();
152
- const url = baseUrl(active);
153
- const session = await readSessionFor(url);
154
- const token = bearer(session);
155
- if (null === token) {
156
- // Name BOTH hosts when there is a session for a different one. "Run reticle login" on its own is
157
- // baffling to somebody who just did — the useful fact is that they logged in somewhere else.
158
- err(null !== active && normalizeUrl(active.url) !== url
159
- ? `signed in to ${normalizeUrl(active.url)}, but this command targets ${url} — run \`reticle connect --url ${url}\`, or set RETICLE_API_KEY`
160
- : `not signed in to ${url} — run \`reticle connect --url ${url}\`, or set RETICLE_API_KEY`);
161
- return 2;
162
- }
163
- const sub = argv[0];
164
- if ('ls' === sub) {
165
- emit(await api('GET', `${url}/v1/projects`, token));
166
- return 0;
167
- }
168
- if ('create' === sub) {
169
- const name = argv.slice(1).join(' ').trim();
170
- if (0 === name.length) {
171
- err('usage: reticle project create <name>');
172
- return 2;
173
- }
174
- const created = CreatedProjectSchema.parse(await api('POST', `${url}/v1/projects`, token, { name }));
175
- emit(created);
176
- hint(`next: \`reticle connect --project ${created.projectId}\` to bind this repo`);
177
- return 0;
178
- }
179
- if ('rename' === sub) {
180
- const id = argv[1];
181
- const name = argv.slice(2).join(' ').trim();
182
- if (id === undefined || 0 === name.length) {
183
- err('usage: reticle project rename <projectId> <new name>');
184
- return 2;
185
- }
186
- emit(await api('PATCH', `${url}/v1/projects/${encodeURIComponent(id)}`, token, { name }));
187
- return 0;
188
- }
189
- if ('rm' === sub || 'delete' === sub) {
190
- const id = argv[1];
191
- if (id === undefined) {
192
- err('usage: reticle project rm <projectId>');
193
- return 2;
194
- }
195
- emit(await api('DELETE', `${url}/v1/projects/${encodeURIComponent(id)}`, token));
196
- return 0;
197
- }
198
- err('usage: reticle project <ls|create <name>|rename <id> <name>|rm <id>>');
199
- return 2;
200
- };
201
105
  const NO_INSTALL = '--no-install';
202
106
  /** Whether `reticle connect`'s init half installs the packages: not under `--no-install`. */
203
107
  export const connectInstalls = (argv) => !argv.includes(NO_INSTALL);
@@ -577,10 +481,6 @@ export const runCloudCommand = async (argv) => {
577
481
  const [cmd, ...rest] = argv;
578
482
  try {
579
483
  switch (cmd) {
580
- case 'login':
581
- // The linker is passed in rather than imported by cloud-login: login ends by linking and
582
- // link needs a session, so the cycle is broken at this one call site.
583
- return await cmdLogin(rest, cmdLink);
584
484
  case 'connect':
585
485
  return await cmdConnect(rest);
586
486
  case 'try':
@@ -590,29 +490,12 @@ export const runCloudCommand = async (argv) => {
590
490
  });
591
491
  case 'logout':
592
492
  return await cmdLogout(rest);
593
- case 'whoami':
594
- return await cmdWhoami();
595
- case 'project':
596
- return await cmdProject(rest);
597
- case 'link':
598
- return await cmdLink(rest);
599
493
  case 'config':
600
494
  return await cmdConfig(rest);
601
- // `push` is the name people type, so it takes `--watch` too: the help and the README say so.
602
- case 'push':
603
- return await cmdSync(rest);
604
495
  case 'sync':
605
496
  return await cmdSync(rest);
606
497
  case 'runs':
607
498
  return await cmdRunsFamily(rest);
608
- case 'issues':
609
- return await cmdIssues(rest);
610
- case 'memory':
611
- return await cmdMemory(rest);
612
- case 'regression':
613
- return await cmdRegression();
614
- case 'share':
615
- return await cmdShare(rest);
616
499
  default:
617
500
  err(`unknown cloud command '${cmd ?? ''}'`);
618
501
  return 2;
@@ -0,0 +1,45 @@
1
+ export declare const leaseFailedLine: (url: string) => string;
2
+ /**
3
+ * Open `url` in a Reticle-leased browser unconditionally: `{ leased }` names the lease, which the
4
+ * caller hands to `releaseLease`; `{ failed }` is the lease's own refusal, as lines to print.
5
+ * `init`'s connect proof on a machine with no system browser takes this path directly.
6
+ */
7
+ export declare function acquireLease(caller: ToolCaller, url: string,
8
+ /** Open it in a window somebody can watch, whatever the daemon was started as. */
9
+ headed?: boolean,
10
+ /** The `.reticle` the lease's runs belong in: the caller's project, not wherever the page resolves. */
11
+ root?: string): Promise<{
12
+ leased: string;
13
+ zeroInstall: boolean;
14
+ } | {
15
+ failed: string[];
16
+ }>;
17
+ /** Hand back a lease `leaseIfNoTab` opened. Best-effort: an expired lease is already gone. */
18
+ export declare function releaseLease(caller: ToolCaller, sessionId: string): Promise<void>;
19
+ /** The narrow slice of an MCP client this needs — one call, then close. */
20
+ export interface ToolCaller {
21
+ call(name: string, args: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
22
+ close(): Promise<void>;
23
+ }
24
+ export declare function endpointFor(port: number, token: string | undefined): URL;
25
+ export declare function connectOverSse(endpoint: URL): Promise<ToolCaller>;
26
+ /**
27
+ * The report object an MCP tool result carries, or undefined when the shape is not one.
28
+ *
29
+ * `field` is the key that makes a payload the report this caller wants: `verified` for a one-shot
30
+ * verdict, `status` for a suite. It is a parameter rather than a fixed name because the suite path
31
+ * reproduced this function's own incident the moment it reused it — the headline printed
32
+ * `unverifiable` while the JSON under it said `"status":"fail"` with 74 failing flows, because a
33
+ * suite report has no `verified` key and the text reader would only accept one that did.
34
+ */
35
+ export declare function verdictOf(result: unknown, field?: string): Record<string, unknown> | undefined;
36
+ /**
37
+ * The tool's own words when it refused, rather than the envelope carrying them.
38
+ *
39
+ * A refusal from these tools is the most useful text they produce — it names the cause and the next
40
+ * action — and printing the MCP result object around it buries that in JSON escaping. The reader is
41
+ * at a terminal; give them the sentence.
42
+ */
43
+ export declare function refusalText(result: unknown): string | undefined;
44
+ /** The payload of a tool result: its structured content, or the JSON object in its text. */
45
+ export declare function payloadOf(result: unknown): Record<string, unknown>;