@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,5 @@
1
+ import { type ToolCaller } from './daemon-link/daemon-client.js';
2
+ export { acquireLease, connectOverSse, endpointFor, refusalText, releaseLease, verdictOf, type ToolCaller, } from './daemon-link/daemon-client.js';
1
3
  /**
2
4
  * A verdict from the CLI, against the daemon that is already running.
3
5
  *
@@ -83,54 +85,12 @@ root?: string): Promise<{
83
85
  } | {
84
86
  failed: string[];
85
87
  }>;
86
- /**
87
- * Open `url` in a Reticle-leased browser unconditionally: `{ leased }` names the lease, which the
88
- * caller hands to `releaseLease`; `{ failed }` is the lease's own refusal, as lines to print.
89
- * `init`'s connect proof on a machine with no system browser takes this path directly.
90
- */
91
- export declare function acquireLease(caller: ToolCaller, url: string,
92
- /** Open it in a window somebody can watch, whatever the daemon was started as. */
93
- headed?: boolean,
94
- /** The `.reticle` the lease's runs belong in: the caller's project, not wherever the page resolves. */
95
- root?: string): Promise<{
96
- leased: string;
97
- zeroInstall: boolean;
98
- } | {
99
- failed: string[];
100
- }>;
101
- /** Hand back a lease `leaseIfNoTab` opened. Best-effort: an expired lease is already gone. */
102
- export declare function releaseLease(caller: ToolCaller, sessionId: string): Promise<void>;
103
88
  /**
104
89
  * The longest one `reticle_assert` call waits: its `timeout_ms` cap, one MCP request less slack.
105
90
  * Named here rather than imported, because this directory does not reach the tool layer; pinned
106
91
  * equal to `MAX_BLOCKING_WAIT_MS` by adhoc-verdict.test.ts so the two cannot drift apart.
107
92
  */
108
93
  export declare const ASSERT_MAX_WAIT_MS = 55000;
109
- /** The narrow slice of an MCP client this needs — one call, then close. */
110
- export interface ToolCaller {
111
- call(name: string, args: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
112
- close(): Promise<void>;
113
- }
114
- export declare function endpointFor(port: number, token: string | undefined): URL;
115
- export declare function connectOverSse(endpoint: URL): Promise<ToolCaller>;
116
- /**
117
- * The report object an MCP tool result carries, or undefined when the shape is not one.
118
- *
119
- * `field` is the key that makes a payload the report this caller wants: `verified` for a one-shot
120
- * verdict, `status` for a suite. It is a parameter rather than a fixed name because the suite path
121
- * reproduced this function's own incident the moment it reused it — the headline printed
122
- * `unverifiable` while the JSON under it said `"status":"fail"` with 74 failing flows, because a
123
- * suite report has no `verified` key and the text reader would only accept one that did.
124
- */
125
- export declare function verdictOf(result: unknown, field?: string): Record<string, unknown> | undefined;
126
- /**
127
- * The tool's own words when it refused, rather than the envelope carrying them.
128
- *
129
- * A refusal from these tools is the most useful text they produce — it names the cause and the next
130
- * action — and printing the MCP result object around it buries that in JSON escaping. The reader is
131
- * at a terminal; give them the sentence.
132
- */
133
- export declare function refusalText(result: unknown): string | undefined;
134
94
  /**
135
95
  * Navigate (optionally) and assert, against the daemon already on this port.
136
96
  *
@@ -146,4 +106,3 @@ export declare function runAdhocVerdict(options: AdhocVerdictOptions): Promise<A
146
106
  * caller that wants the finished report asks again by `runId` rather than starting a second drive.
147
107
  */
148
108
  export declare function exploreToEnd(caller: ToolCaller, args: Record<string, unknown>, budgetMs: number, now?: () => number): Promise<unknown>;
149
- export {};
@@ -1,19 +1,8 @@
1
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
- import { CLI_CLIENT_NAME } from '../../judgement/runs/verification-sync.js';
3
- import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
4
- import { DEFAULT_ASSERT_TIMEOUT_MS, LOOPBACK_HOST, MCP_SSE_PATH } from '@reticlehq/core';
5
- import { TOKEN_QUERY_PARAM } from '../../portal/bridge/token-auth.js';
6
- import { ReticleTool, asString as asText } from '@reticlehq/core';
1
+ import { DEFAULT_ASSERT_TIMEOUT_MS, ReticleTool, asString as asText } from '@reticlehq/core';
7
2
  import { decideOpen } from './launch/cli-launch.js';
8
- /** The lease tool is reached through `reticle_run`, the route that works on every tool profile. */
9
- const LeaseAction = { ACQUIRE: 'acquire', RELEASE: 'release' };
10
- /**
11
- * Generous on purpose: on a machine with no Chrome, Edge or Playwright Chromium the FIRST lease
12
- * downloads Chromium before it can open anything (see launch-chromium), which outlives the MCP
13
- * client's 60s default and would report a timeout for a lease that was about to succeed.
14
- */
15
- const LEASE_TIMEOUT_MS = 300_000;
16
- const leaseFailedLine = (url) => `could not open ${url} in a Reticle browser, so nothing was checked:`;
3
+ import { acquireLease, connectOverSse, endpointFor, refusalText, releaseLease, verdictOf, } from './daemon-link/daemon-client.js';
4
+ // Re-exported so every existing importer of the daemon link through this file is unaffected.
5
+ export { acquireLease, connectOverSse, endpointFor, refusalText, releaseLease, verdictOf, } from './daemon-link/daemon-client.js';
17
6
  /**
18
7
  * Open `url` in a Reticle-leased browser when the daemon has no tab on its origin.
19
8
  *
@@ -64,48 +53,6 @@ function sameDocument(a, b) {
64
53
  return false;
65
54
  }
66
55
  }
67
- /**
68
- * Open `url` in a Reticle-leased browser unconditionally: `{ leased }` names the lease, which the
69
- * caller hands to `releaseLease`; `{ failed }` is the lease's own refusal, as lines to print.
70
- * `init`'s connect proof on a machine with no system browser takes this path directly.
71
- */
72
- export async function acquireLease(caller, url,
73
- /** Open it in a window somebody can watch, whatever the daemon was started as. */
74
- headed,
75
- /** The `.reticle` the lease's runs belong in: the caller's project, not wherever the page resolves. */
76
- root) {
77
- const acquired = await caller.call(ReticleTool.RUN, {
78
- tool: ReticleTool.LEASE,
79
- args: {
80
- action: LeaseAction.ACQUIRE,
81
- url,
82
- ...(true === headed ? { headed } : {}),
83
- ...(root === undefined ? {} : { root }),
84
- },
85
- }, LEASE_TIMEOUT_MS);
86
- const report = verdictOf(acquired, 'sessionId');
87
- const sessionId = report?.['sessionId'];
88
- // `zeroInstall`: the page never dialled in, so the lease supplied Reticle's own reader.
89
- if ('string' === typeof sessionId) {
90
- return { leased: sessionId, zeroInstall: true === report?.['zeroInstall'] };
91
- }
92
- const why = verdictOf(acquired, 'error')?.['error'];
93
- return {
94
- failed: [
95
- leaseFailedLine(url),
96
- 'string' === typeof why ? why : (refusalText(acquired) ?? JSON.stringify(acquired)),
97
- ],
98
- };
99
- }
100
- /** Hand back a lease `leaseIfNoTab` opened. Best-effort: an expired lease is already gone. */
101
- export async function releaseLease(caller, sessionId) {
102
- await caller
103
- .call(ReticleTool.RUN, {
104
- tool: ReticleTool.LEASE,
105
- args: { action: LeaseAction.RELEASE, sessionId },
106
- })
107
- .catch(() => undefined);
108
- }
109
56
  /** How long `--expect` waits without a `--timeout`: the wait form's own default. */
110
57
  const DEFAULT_EXPECT_WAIT_MS = DEFAULT_ASSERT_TIMEOUT_MS;
111
58
  /** Room for the daemon to answer after the wait it was given has run out. */
@@ -125,31 +72,55 @@ function runRefusal(result) {
125
72
  const said = verdictOf(result, 'error')?.['error'];
126
73
  return refusalText(result) ?? ('string' === typeof said ? said : undefined);
127
74
  }
75
+ /** The `reticle_assert` action that waits for a predicate within a budget. */
76
+ const ASSERT_WAIT_ACTION = 'wait';
77
+ /**
78
+ * The refusal of a daemon whose `reticle_assert` takes no `action`: every surface but the merged
79
+ * one, where `reticle_wait_for` is still a tool of its own.
80
+ */
81
+ const REFUSES_ACTION = /unknown parameter[^\n]*\baction\b/i;
82
+ /**
83
+ * One wait for the predicate, through `reticle_assert { action: "wait" }` (#2144).
84
+ *
85
+ * It went through `reticle_run { tool: "reticle_wait_for" }`. That name was merged into
86
+ * `reticle_assert`, and a 3.9.0 daemon answered it with "reticle_wait_for was merged into
87
+ * reticle_assert", so every `--expect` with a long `--timeout` printed `verified: unknown`. A daemon
88
+ * whose `reticle_assert` takes no action still has `reticle_wait_for` as a tool, so that is the
89
+ * one place the name is still used.
90
+ */
91
+ async function waitOnce(caller, args, timeoutMs) {
92
+ const waited = await caller.call(ReticleTool.ASSERT, { action: ASSERT_WAIT_ACTION, ...args }, timeoutMs);
93
+ const refused = runRefusal(waited);
94
+ if (refused === undefined || !REFUSES_ACTION.test(refused)) {
95
+ return waited;
96
+ }
97
+ return caller.call(ReticleTool.RUN, { tool: ReticleTool.WAIT_FOR, args }, timeoutMs);
98
+ }
128
99
  /**
129
100
  * Wait for the expectation, then grade it with ONE `reticle_assert` (#1533).
130
101
  *
131
- * The grade is `reticle_assert`'s, never the wait's: it journals, captures the assertion, and
132
- * carries `verifiedReason`, contradictions and coverage, which `reticle_wait_for` does not.
133
- * `reticle_assert` already waits when `timeout_ms > 0`, up to one request. A longer budget spends
134
- * the part one assert cannot hold in `reticle_wait_for` first, through `reticle_run` (the route
135
- * every tool profile has), following `resume_ms`, and then asserts. No `since` is passed by
136
- * either: both default to the same last-act cursor, and nothing here acts in between.
102
+ * The grade is the final `reticle_assert`'s: it journals, captures the assertion, and carries
103
+ * `verifiedReason`, contradictions and coverage. `reticle_assert` already waits when
104
+ * `timeout_ms > 0`, up to one request. A longer budget spends the part one assert cannot hold in
105
+ * `reticle_assert { action: "wait" }` first, following `resume_ms`, and then asserts. No `since` is
106
+ * passed by either: both default to the same last-act cursor, and nothing here acts in between.
137
107
  */
138
108
  async function waitForExpectation(caller, predicate, pin, timeoutMs) {
139
109
  const budget = Math.max(0, timeoutMs ?? DEFAULT_EXPECT_WAIT_MS);
140
110
  let settle = budget - ASSERT_MAX_WAIT_MS;
141
111
  while (0 < settle) {
142
- const waited = await caller.call(ReticleTool.RUN, { tool: ReticleTool.WAIT_FOR, args: { predicate, timeout_ms: settle, ...pin } },
143
- // The daemon blocks no longer than one request allows and answers resume_ms past that, so
144
- // the budget plus slack always outlasts the call.
145
- settle + WAIT_CALL_SLACK_MS);
112
+ // The daemon blocks no longer than one request allows and answers resume_ms past that, so the
113
+ // budget plus slack always outlasts the call.
114
+ const waited = await waitOnce(caller, { predicate, timeout_ms: settle, ...pin }, settle + WAIT_CALL_SLACK_MS);
146
115
  const refused = runRefusal(waited);
147
- if (refused !== undefined)
116
+ if (refused !== undefined) {
148
117
  return { refused };
149
- // By `pass`, which every wait answers with: a "not yet" carries no `verified`.
150
- const report = verdictOf(waited, 'pass');
118
+ }
119
+ // In the shape `reticle_assert` answers: a final answer carries `verified`, and only a wait
120
+ // that the per-call cap stopped carries `resume_ms` without one.
121
+ const report = verdictOf(waited, 'resume_ms');
151
122
  const resume = report?.['resume_ms'];
152
- if (true === report?.['pass'] ||
123
+ if (report?.['verified'] !== undefined ||
153
124
  'number' !== typeof resume ||
154
125
  resume <= 0 ||
155
126
  resume >= settle) {
@@ -164,91 +135,6 @@ async function waitForExpectation(caller, predicate, pin, timeoutMs) {
164
135
  }
165
136
  /** `verified` is the only field that decides the exit code. Everything else is for the reader. */
166
137
  const PROVED = 'yes';
167
- export function endpointFor(port, token) {
168
- const url = new URL(`http://${LOOPBACK_HOST}:${String(port)}${MCP_SSE_PATH}`);
169
- if (token !== undefined && token.length > 0)
170
- url.searchParams.set(TOKEN_QUERY_PARAM, token);
171
- return url;
172
- }
173
- export async function connectOverSse(endpoint) {
174
- const client = new Client({ name: CLI_CLIENT_NAME, version: '1' }, { capabilities: {} });
175
- await client.connect(new SSEClientTransport(endpoint));
176
- return {
177
- call: async (name, args, timeoutMs) => await client.callTool({ name, arguments: args }, undefined, timeoutMs === undefined ? undefined : { timeout: timeoutMs }),
178
- close: async () => {
179
- await client.close();
180
- },
181
- };
182
- }
183
- /**
184
- * The report object an MCP tool result carries, or undefined when the shape is not one.
185
- *
186
- * `field` is the key that makes a payload the report this caller wants: `verified` for a one-shot
187
- * verdict, `status` for a suite. It is a parameter rather than a fixed name because the suite path
188
- * reproduced this function's own incident the moment it reused it — the headline printed
189
- * `unverifiable` while the JSON under it said `"status":"fail"` with 74 failing flows, because a
190
- * suite report has no `verified` key and the text reader would only accept one that did.
191
- */
192
- export function verdictOf(result, field = 'verified') {
193
- const structured = result?.structuredContent;
194
- if ('object' === typeof structured && null !== structured) {
195
- return structured;
196
- }
197
- // A daemon that answers with the verdict as TEXT left this undefined, so the headline read
198
- // `verified: unknown` while the JSON printed underneath it said `"verified":"no"` — one response
199
- // giving two answers to the same question. The text is the same object, so read it.
200
- return verdictFromText(result, field);
201
- }
202
- /** The report object inside an MCP text part, when the result carried it there instead. */
203
- function verdictFromText(result, field) {
204
- const content = result?.content;
205
- if (!Array.isArray(content))
206
- return undefined;
207
- for (const part of content) {
208
- const text = part.text;
209
- if ('string' !== typeof text)
210
- continue;
211
- try {
212
- const parsed = JSON.parse(text);
213
- // Only a shape that actually carries a verdict — anything else stays `unknown`, which is the
214
- // honest answer for a result this function could not read.
215
- if ('object' === typeof parsed && null !== parsed && field in parsed) {
216
- return parsed;
217
- }
218
- }
219
- catch {
220
- /* Not JSON; the next part may still be. */
221
- }
222
- }
223
- return undefined;
224
- }
225
- /**
226
- * The tool's own words when it refused, rather than the envelope carrying them.
227
- *
228
- * A refusal from these tools is the most useful text they produce — it names the cause and the next
229
- * action — and printing the MCP result object around it buries that in JSON escaping. The reader is
230
- * at a terminal; give them the sentence.
231
- */
232
- export function refusalText(result) {
233
- const r = result;
234
- if (true !== r?.isError || !Array.isArray(r.content))
235
- return undefined;
236
- const text = r.content
237
- .map((part) => part.text)
238
- .filter((t) => 'string' === typeof t)
239
- .join('\n');
240
- if (0 === text.length)
241
- return undefined;
242
- // The tools answer with a JSON envelope whose `error` is the sentence; unwrap when it is one.
243
- try {
244
- const parsed = JSON.parse(text);
245
- const inner = parsed?.error;
246
- return 'string' === typeof inner ? inner : text;
247
- }
248
- catch {
249
- return text;
250
- }
251
- }
252
138
  /**
253
139
  * Navigate (optionally) and assert, against the daemon already on this port.
254
140
  *
@@ -288,8 +174,9 @@ export async function runAdhocVerdict(options) {
288
174
  }
289
175
  if (url !== undefined && options.sessionId === undefined && options.sessions !== undefined) {
290
176
  const opened = await leaseIfNoTab(caller, url, options.sessions, options.root);
291
- if ('failed' in opened)
177
+ if ('failed' in opened) {
292
178
  return { code: 1, lines: ['verified: unknown', ...opened.failed] };
179
+ }
293
180
  leased = opened.leased;
294
181
  alreadyAt = true === opened.alreadyAt;
295
182
  }
@@ -301,8 +188,9 @@ export async function runAdhocVerdict(options) {
301
188
  await caller.call(ReticleTool.NAVIGATE, { url, ...pin });
302
189
  }
303
190
  const waited = await waitForExpectation(caller, options.predicate, pin, options.timeoutMs);
304
- if ('refused' in waited)
191
+ if ('refused' in waited) {
305
192
  return { code: 1, lines: ['verified: unknown', waited.refused] };
193
+ }
306
194
  const result = waited.result;
307
195
  const refusal = refusalText(result);
308
196
  if (refusal !== undefined) {
@@ -330,8 +218,9 @@ export async function runAdhocVerdict(options) {
330
218
  };
331
219
  }
332
220
  finally {
333
- if (leased !== undefined)
221
+ if (leased !== undefined) {
334
222
  await releaseLease(caller, leased);
223
+ }
335
224
  await caller.close().catch(() => undefined);
336
225
  }
337
226
  }
@@ -357,8 +246,9 @@ export async function exploreToEnd(caller, args, budgetMs, now = Date.now) {
357
246
  for (;;) {
358
247
  const report = verdictOf(result, 'status');
359
248
  const runId = report?.['runId'];
360
- if (DRIVE_RUNNING !== report?.['status'] || 'string' !== typeof runId || now() >= deadline)
249
+ if (DRIVE_RUNNING !== report?.['status'] || 'string' !== typeof runId || now() >= deadline) {
361
250
  return result;
251
+ }
362
252
  result = await ask({ runId });
363
253
  }
364
254
  }
@@ -1,3 +1,4 @@
1
+ import { GateOutcome } from '@reticlehq/core/telemetry';
1
2
  /**
2
3
  * What `reticle gate` means by its exit code.
3
4
  *
@@ -21,14 +22,17 @@ export declare const GateExit: {
21
22
  /** The platform knows a flow that passed before and is broken now: `reticle runs regression`'s code. */
22
23
  readonly REGRESSION: 3;
23
24
  };
25
+ export type GateExit = (typeof GateExit)[keyof typeof GateExit];
26
+ /** The `gate_completed` outcome for each exit code. A gate that throws reports `error` instead. */
27
+ export declare const GATE_OUTCOME: Readonly<Record<GateExit, GateOutcome>>;
24
28
  /**
25
29
  * The gate's code once the platform has been asked about regressions: a known one fails the build
26
30
  * with `reticle runs regression`'s code, whatever the local answer was, so the gate is the one
27
31
  * command CI needs. `undefined` (not linked, or the platform did not answer) changes nothing.
28
32
  */
29
- export declare function gateExitWith(local: number, regressed: number | undefined,
33
+ export declare function gateExitWith(local: GateExit, regressed: number | undefined,
30
34
  /** The platform the project is linked to refused its credential: the gate fails closed. */
31
- refused?: boolean): number;
35
+ refused?: boolean): GateExit;
32
36
  /**
33
37
  * Said when the linked platform refused the key. A CI pipeline whose key was revoked stayed green
34
38
  * and silent: the gate lost the platform's regression report and the only trace was a log line.
@@ -1,3 +1,4 @@
1
+ import { GateOutcome } from '@reticlehq/core/telemetry';
1
2
  /**
2
3
  * What `reticle gate` means by its exit code.
3
4
  *
@@ -21,6 +22,13 @@ export const GateExit = {
21
22
  /** The platform knows a flow that passed before and is broken now: `reticle runs regression`'s code. */
22
23
  REGRESSION: 3,
23
24
  };
25
+ /** The `gate_completed` outcome for each exit code. A gate that throws reports `error` instead. */
26
+ export const GATE_OUTCOME = {
27
+ [GateExit.PASS]: GateOutcome.PASS,
28
+ [GateExit.FAIL]: GateOutcome.FAIL,
29
+ [GateExit.NOTHING_TO_CHECK]: GateOutcome.NOTHING_TO_CHECK,
30
+ [GateExit.REGRESSION]: GateOutcome.REGRESSION,
31
+ };
24
32
  /**
25
33
  * The gate's code once the platform has been asked about regressions: a known one fails the build
26
34
  * with `reticle runs regression`'s code, whatever the local answer was, so the gate is the one
@@ -29,8 +37,9 @@ export const GateExit = {
29
37
  export function gateExitWith(local, regressed,
30
38
  /** The platform the project is linked to refused its credential: the gate fails closed. */
31
39
  refused = false) {
32
- if (regressed !== undefined && 0 < regressed)
40
+ if (regressed !== undefined && 0 < regressed) {
33
41
  return GateExit.REGRESSION;
42
+ }
34
43
  return refused && GateExit.PASS === local ? GateExit.FAIL : local;
35
44
  }
36
45
  /**
@@ -52,8 +61,9 @@ export const MSG_GATE_NO_FLOWS = 'Reticle gate: no saved flows here, so there wa
52
61
  */
53
62
  export function msgGateRegression(broken) {
54
63
  const names = broken.map((entry) => {
55
- if ('string' === typeof entry)
64
+ if ('string' === typeof entry) {
56
65
  return entry;
66
+ }
57
67
  const record = 'object' === typeof entry && null !== entry ? entry : {};
58
68
  const name = record['flowName'] ?? record['name'];
59
69
  const was = record['previousStatus'];
@@ -0,0 +1,2 @@
1
+ /** The same connection hint for a running daemon in `status` and `doctor`. */
2
+ export declare function mcpConnectionHint(port: number): string;
@@ -0,0 +1,6 @@
1
+ import { LOOPBACK_HOST, MCP_SSE_PATH } from '@reticlehq/core';
2
+ const MCP_CONNECTION_HINT = 'If your agent has no reticle_* tools, connect over HTTP/SSE at';
3
+ /** The same connection hint for a running daemon in `status` and `doctor`. */
4
+ export function mcpConnectionHint(port) {
5
+ return `${MCP_CONNECTION_HINT} http://${LOOPBACK_HOST}:${String(port)}${MCP_SSE_PATH}`;
6
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * One line at the end of `reticle gate`, `reticle verify` and `reticle affected` when they run in a
3
+ * CI runner or a container. It names the pull request check and the page that tells more about it.
4
+ *
5
+ * Rules: one line; never in an interactive terminal; never in a machine output mode; off with
6
+ * `RETICLE_NO_CI_HINT`. It goes to stderr. The stdout of `verify` carries the verdict lines that a
7
+ * pipeline reads, and stderr already carries the human notes of these commands.
8
+ *
9
+ * No network call. The latest version comes from the update manifest that the update check already
10
+ * caches on disk. When there is no cache, the line does not compare versions.
11
+ *
12
+ * The caller gives the automation hint (`currentAutomationHint`): `telemetry` already reaches this
13
+ * directory, so this directory does not reach back.
14
+ */
15
+ import { AutomationHint } from '@reticlehq/core/telemetry';
16
+ /** Set to `1` to stop the line. */
17
+ export declare const CI_HINT_SILENCE_ENV = "RETICLE_NO_CI_HINT";
18
+ /** The page that tells about the pull request check. */
19
+ export declare const CI_HINT_URL = "https://reticle.sh/ci";
20
+ export interface CiHintInput {
21
+ env: NodeJS.ProcessEnv;
22
+ automation: AutomationHint | undefined;
23
+ /** A person watches the output in a terminal. */
24
+ interactive: boolean;
25
+ /** The command writes output for a program to read (JSON, an agent hook). */
26
+ machineOutput: boolean;
27
+ currentVersion: string;
28
+ /** The latest version from the cached update check, if there is one. */
29
+ latestVersion: string | undefined;
30
+ }
31
+ /** The line, or undefined when it must not print. Pure. */
32
+ export declare function ciHintLine(input: CiHintInput): string | undefined;
33
+ export interface CiHintPorts {
34
+ env: NodeJS.ProcessEnv;
35
+ interactive: () => boolean;
36
+ latestVersion: () => string | undefined;
37
+ write: (text: string) => void;
38
+ }
39
+ /** Write the line to stderr when it applies. Call it last, after the command's own output. */
40
+ export declare function writeCiHint(args: {
41
+ automation: AutomationHint | undefined;
42
+ machineOutput: boolean;
43
+ ports?: CiHintPorts;
44
+ }): void;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * One line at the end of `reticle gate`, `reticle verify` and `reticle affected` when they run in a
3
+ * CI runner or a container. It names the pull request check and the page that tells more about it.
4
+ *
5
+ * Rules: one line; never in an interactive terminal; never in a machine output mode; off with
6
+ * `RETICLE_NO_CI_HINT`. It goes to stderr. The stdout of `verify` carries the verdict lines that a
7
+ * pipeline reads, and stderr already carries the human notes of these commands.
8
+ *
9
+ * No network call. The latest version comes from the update manifest that the update check already
10
+ * caches on disk. When there is no cache, the line does not compare versions.
11
+ *
12
+ * The caller gives the automation hint (`currentAutomationHint`): `telemetry` already reaches this
13
+ * directory, so this directory does not reach back.
14
+ */
15
+ import { AutomationHint } from '@reticlehq/core/telemetry';
16
+ import { parseMajor } from '@reticlehq/init';
17
+ import { loadManifest } from '../update/update-checker.js';
18
+ import { SERVER_VERSION } from '../version/identity/server-version.js';
19
+ /** Set to `1` to stop the line. */
20
+ export const CI_HINT_SILENCE_ENV = 'RETICLE_NO_CI_HINT';
21
+ /** The page that tells about the pull request check. */
22
+ export const CI_HINT_URL = 'https://reticle.sh/ci';
23
+ /** The env values that do not turn the line off. */
24
+ const NOT_SILENCED = new Set(['', '0', 'false']);
25
+ /** The automation hints that get the line. A hosted workspace or an agent sandbox is a person. */
26
+ const CI_HINT_AUTOMATION = new Set([
27
+ AutomationHint.CI_RUNNER,
28
+ AutomationHint.CONTAINER,
29
+ ]);
30
+ const CiHintCopy = {
31
+ CHECK: `Reticle: a pull request check shows each replay, the run history and a merge block. Read ${CI_HINT_URL}`,
32
+ BEHIND: (current, latest) => `Reticle: this engine (${current}) is a major version behind ${latest}. Update it, then read ${CI_HINT_URL}`,
33
+ };
34
+ /** The line, or undefined when it must not print. Pure. */
35
+ export function ciHintLine(input) {
36
+ if (!NOT_SILENCED.has(input.env[CI_HINT_SILENCE_ENV] ?? '')) {
37
+ return undefined;
38
+ }
39
+ if (input.interactive || input.machineOutput) {
40
+ return undefined;
41
+ }
42
+ if (input.automation === undefined || !CI_HINT_AUTOMATION.has(input.automation)) {
43
+ return undefined;
44
+ }
45
+ const current = parseMajor(input.currentVersion);
46
+ const latest = parseMajor(input.latestVersion);
47
+ if (current !== undefined && latest !== undefined && latest > current) {
48
+ return CiHintCopy.BEHIND(input.currentVersion, input.latestVersion ?? '');
49
+ }
50
+ return CiHintCopy.CHECK;
51
+ }
52
+ const realPorts = () => ({
53
+ env: process.env,
54
+ interactive: () => true === process.stdout.isTTY || true === process.stderr.isTTY,
55
+ latestVersion: () => loadManifest()?.latestVersion,
56
+ write: (text) => process.stderr.write(text),
57
+ });
58
+ /** Write the line to stderr when it applies. Call it last, after the command's own output. */
59
+ export function writeCiHint(args) {
60
+ const ports = args.ports ?? realPorts();
61
+ const line = ciHintLine({
62
+ env: ports.env,
63
+ automation: args.automation,
64
+ interactive: ports.interactive(),
65
+ machineOutput: args.machineOutput,
66
+ currentVersion: SERVER_VERSION,
67
+ latestVersion: ports.latestVersion(),
68
+ });
69
+ if (line !== undefined) {
70
+ ports.write(`${line}\n`);
71
+ }
72
+ }
@@ -1 +1,6 @@
1
- export declare function handleDoctor(port: number): Promise<void>;
1
+ import type { StepReporter } from './setup-mcp-cli.js';
2
+ /**
3
+ * `reticle doctor`, and with `fix`, `reticle doctor --fix`. Without `fix` it changes nothing and
4
+ * ends with how many rows `--fix` can repair. With `fix` the exit code is 1 while any row fails.
5
+ */
6
+ export declare function handleDoctor(port: number, fix?: boolean, reportStep?: StepReporter): Promise<void>;
@@ -11,7 +11,8 @@ import { daemonLine } from './doctor/doctor-daemon-line.js';
11
11
  import { projectWiringLine } from './doctor/doctor-project-line.js';
12
12
  import { hasProjectConnectedBefore } from '../../memory/recall/prior/connection-memory.js';
13
13
  import { sessionsLine } from './doctor/doctor-sessions-line.js';
14
- import { captureLookup, describeForeignHolder, findPortHolder } from './ports/port-holder.js';
14
+ import { captureLookup, describeForeignHolder } from './ports/port-holder.js';
15
+ import { identifyPortHolder } from '../daemon/port-state.js';
15
16
  import { chromiumHint } from './doctor/browser/chromium-hint.js';
16
17
  import { probeChromiumWithFallback } from '../../launch-chromium.js';
17
18
  import { SERVER_VERSION } from '../version/identity/server-version.js';
@@ -32,6 +33,8 @@ import { describeUnsynced } from '../../memory/cloud/unsynced-roots.js';
32
33
  import { machineUnsyncedRoots } from '../../memory/project/sync-status.js';
33
34
  import { attachState, describeAttachState } from '../../surface/mcp/attach-memory.js';
34
35
  import { agentLinkText } from './doctor/doctor-agent-link.js';
36
+ import { countNeeded, fixFooter, runDoctorFix } from './doctor/doctor-fix.js';
37
+ import { machineRepairs, mcpRegistrationRow, readLauncherLine, readMcpRegistration, } from './doctor-repairs.js';
35
38
  import { findOccupiedSiblings, siblingListenerNote } from './ports/sibling-ports.js';
36
39
  import { daemonsServingProjectElsewhere, resolveDaemonForProject, splitBrainNote, wrongDaemonNote, } from '../daemon/daemon-resolve.js';
37
40
  import { isAlive } from '../daemon/daemon.js';
@@ -70,12 +73,44 @@ function asIdentity(payload) {
70
73
  */
71
74
  function splitBrainLine(port, projectId) {
72
75
  const home = reticleStateHome();
73
- const elsewhere = splitBrainNote(port, daemonsServingProjectElsewhere(projectId, port, home, isAlive, (other) => hasProjectConnectedBefore(home, other, projectId)));
76
+ const elsewhere = splitBrainNote(port, daemonsServingProjectElsewhere(projectId, port, home, isAlive, (other) => hasProjectConnectedBefore(home, other, projectId)), { projectId, home });
74
77
  return elsewhere ?? wrongDaemonNote(port, resolveDaemonForProject(projectId, home, isAlive));
75
78
  }
76
- export async function handleDoctor(port) {
79
+ const writeLine = (s) => {
80
+ process.stdout.write(`${s}\n`);
81
+ };
82
+ /**
83
+ * `reticle doctor`, and with `fix`, `reticle doctor --fix`. Without `fix` it changes nothing and
84
+ * ends with how many rows `--fix` can repair. With `fix` the exit code is 1 while any row fails.
85
+ */
86
+ export async function handleDoctor(port, fix = false, reportStep = () => { }) {
87
+ const repairs = machineRepairs(port, reportStep);
88
+ if (fix) {
89
+ const green = await runDoctorFix({
90
+ doctor: (show) => doctorLines(port, show),
91
+ repairs,
92
+ print: writeLine,
93
+ });
94
+ if (!green) {
95
+ process.exitCode = 1;
96
+ }
97
+ return;
98
+ }
99
+ await doctorLines(port, true);
100
+ const footer = fixFooter(await countNeeded(repairs));
101
+ if (footer !== undefined) {
102
+ writeLine('');
103
+ writeLine(footer);
104
+ }
105
+ }
106
+ /** Every doctor row, in order. Each row is printed as it is found when `show` is true. */
107
+ async function doctorLines(port, show) {
108
+ const lines = [];
77
109
  const line = (s) => {
78
- process.stdout.write(`${s}\n`);
110
+ lines.push(s);
111
+ if (show) {
112
+ writeLine(s);
113
+ }
79
114
  };
80
115
  line('reticle doctor');
81
116
  line(doctorRow(DoctorRow.NODE, process.version));
@@ -84,6 +119,7 @@ export async function handleDoctor(port) {
84
119
  let sessions;
85
120
  let daemonStatus;
86
121
  line(doctorRow(DoctorRow.CHROMIUM, chromiumHint(await probeChromiumWithFallback())));
122
+ line(mcpRegistrationRow(readMcpRegistration()));
87
123
  // Ask the PORT, not just the pid file. "not running on :4400" has been printed about a port that
88
124
  // was demonstrably occupied, which sends the reader to start a daemon that cannot bind. The three
89
125
  // states are genuinely different problems with different fixes, so doctor names which one it is.
@@ -126,7 +162,7 @@ export async function handleDoctor(port) {
126
162
  // pipeline that also kills the agent's own MCP proxy.
127
163
  // `pid` is what our pid file recorded for this port. When it matches the process actually
128
164
  // holding it, this is our OWN daemon wedged, not a stranger — and the fix is different.
129
- line(doctorRow(DoctorRow.DAEMON, `✗ ${describeForeignHolder(port, findPortHolder(port, captureLookup), pid)}`));
165
+ line(doctorRow(DoctorRow.DAEMON, `✗ ${describeForeignHolder(port, identifyPortHolder(port, captureLookup), pid)}`));
130
166
  }
131
167
  else {
132
168
  line(doctorRow(DoctorRow.DAEMON, `✗ not running on :${port} — your agent runs \`reticle mcp\` (or \`reticle serve\`)`));
@@ -167,6 +203,10 @@ export async function handleDoctor(port) {
167
203
  // would flip agent presence on a daemon with no agents attached.
168
204
  const foreign = presence === PortPresence.FOREIGN;
169
205
  line(doctorRow(DoctorRow.AGENT_LINK, agentLinkText({ split, attachAction, foreign })));
206
+ const launcher = readLauncherLine(process.cwd());
207
+ if (launcher !== undefined) {
208
+ line(doctorRow(DoctorRow.LAUNCHER, launcher));
209
+ }
170
210
  // Remaining half of #261. The SDK dialling a port we are not on is invisible as a refused inbound,
171
211
  // but a listener on a well-known Reticle port is visible. Report the observation; do not conclude
172
212
  // it is the daemon this app wants — somebody else's daemon looks the same from here.
@@ -263,6 +303,7 @@ export async function handleDoctor(port) {
263
303
  if (keychain !== undefined) {
264
304
  line(doctorRow(DoctorRow.DESKTOP, `⚠ ${keychain}`));
265
305
  }
306
+ return lines;
266
307
  }
267
308
  /**
268
309
  * Two rows: does each agent hand its prompts to Reticle, and does the daemon send this