@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
@@ -8,12 +8,13 @@
8
8
  * on stable, human-authored substrings of the thrown messages (see session-manager.ts, the flow/
9
9
  * baseline stores). No clock, no IO — unit-testable in isolation.
10
10
  */
11
- import { CodedRefusalSchema, PRODUCTION_REFUSAL_PREFIX, RefusalCode, RefusalReason, ReticleEnv, TRANSPORT_LIMITS, UNISSUED_REF_REFUSAL, } from '@reticlehq/core';
11
+ import { CodedRefusalSchema, PRODUCTION_REFUSAL_PREFIX, RefusalCode, RefusalDetail, RefusalReason, ReticleEnv, TRANSPORT_LIMITS, UNISSUED_REF_REFUSAL, } from '@reticlehq/core';
12
12
  import { SELF_RECOVERING_MARKER } from '../../portal/session/no-session-diagnosis.js';
13
13
  import { MSG_HARNESS_DISABLED, MSG_HARNESS_UNCLAIMED, MSG_HARNESS_UNCONFIRMED, PlatformRefusalCode, RefusalLead, } from '../../features/harness/platform/refusal.js';
14
14
  import { BODY_CLAUSE_REFUSAL_OPENING } from '@reticlehq/engine/evidence/body-capture-remedy.js';
15
15
  import { EMPTY_FIND_OPENING } from './read/query-shape.js';
16
16
  import { UNKNOWN_FLOW_OPENING } from '../../language/flows/suite-selection.js';
17
+ import { ARTIFACT_ROOT_ABSOLUTE_REFUSAL, ARTIFACT_ROOT_MISSING_REFUSAL, } from '../../memory/project/root-claims.js';
17
18
  import { CHROMIUM_PATH_REFUSAL_PREFIX, chromiumInstallCommand, bundledPlaywrightVersion, } from '../../command/cli/doctor/browser/chromium-hint.js';
18
19
  /** Marks where an over-long message had its middle removed. Named so tests assert it by value. */
19
20
  const ELISION = '… [elided] …';
@@ -32,8 +33,9 @@ const ELISION = '… [elided] …';
32
33
  */
33
34
  function capMessage(message) {
34
35
  const max = TRANSPORT_LIMITS.MAX_ERROR_LENGTH;
35
- if (max >= message.length)
36
+ if (max >= message.length) {
36
37
  return message;
38
+ }
37
39
  const keep = Math.floor((max - ELISION.length) / 2);
38
40
  return `${message.slice(0, keep)}${ELISION}${message.slice(message.length - keep)}`;
39
41
  }
@@ -70,6 +72,11 @@ const SELF_RECOVERING = [
70
72
  // Both are the caller's to fix, and the defect ask on top told an agent to file a bug about them.
71
73
  { match: literally(EMPTY_FIND_OPENING), reason: RefusalReason.BAD_ARGS },
72
74
  { match: literally(UNKNOWN_FLOW_OPENING), reason: RefusalReason.NO_MATCH },
75
+ // A lease `root` naming no directory on disk — relative, or absent — is the caller's to fix, and
76
+ // the message carries the exact path to pass instead. Both sat under the feedback ask, which
77
+ // invited a bug report about a mistyped path (#2085).
78
+ { match: literally(ARTIFACT_ROOT_ABSOLUTE_REFUSAL), reason: RefusalReason.BAD_ARGS },
79
+ { match: literally(ARTIFACT_ROOT_MISSING_REFUSAL), reason: RefusalReason.BAD_ARGS },
73
80
  ];
74
81
  /**
75
82
  * The refusal for a `ref` Reticle never minted — decided BEFORE every other rule, not by one inside
@@ -83,8 +90,9 @@ function isUnissuedRefRefusal(message) {
83
90
  }
84
91
  /** A message that already carries its own concrete next action, so nothing should be appended. */
85
92
  function isSelfRecovering(message) {
86
- if (message.includes(SELF_RECOVERING_MARKER))
93
+ if (message.includes(SELF_RECOVERING_MARKER)) {
87
94
  return true;
95
+ }
88
96
  return SELF_RECOVERING.some((rule) => rule.match.test(message));
89
97
  }
90
98
  /** The recovery hints, named so they are not free strings and can be asserted in tests. */
@@ -142,8 +150,9 @@ export const RECOVERY = {
142
150
  * carry: which of them to reach for, and that nothing was acted on.
143
151
  */
144
152
  TARGET_MISSED: 'The call was valid — the selector matched nothing on the page RIGHT NOW, and nothing was ' +
145
- 'acted on. Take a reticle_snapshot to see what is actually there (a view may still be ' +
146
- 'rendering, or the control may be named differently), then retry with what it shows. This is ' +
153
+ 'acted on. If the message lists the nearest targets and one is the element you meant, retry ' +
154
+ 'with its ref. If not, take a reticle_snapshot to see what is actually there (a view may still ' +
155
+ 'be rendering, or the control may be named differently), then retry with what it shows. This is ' +
147
156
  'a miss, not a Reticle defect: there is nothing to report.',
148
157
  /**
149
158
  * The selector was underspecified, which is the caller's to fix and the same class as a miss.
@@ -201,10 +210,12 @@ export const RECOVERY = {
201
210
  'on purpose. If you mean it, send the SAME call again with ' +
202
211
  '`args: { confirmDangerous: true }` — it goes inside the `args` object, not beside it. This is ' +
203
212
  'a deliberate refusal, not a defect: there is nothing to report.',
204
- UNSUPPORTED_SURFACE: 'That surface is not supported yet, and Reticle refuses rather than pretending — a rich-text ' +
205
- 'editor keeps its own document model, so writing to the DOM would look right and submit the old ' +
206
- 'content. Drive the same outcome another way (a plain input, a command, a keyboard action) or ' +
207
- 'assert on the result instead of typing it. Known gap, already reported: no need to file it.',
213
+ UNSUPPORTED_SURFACE: 'That surface is a rich-text editor: it keeps its own document model, so `fill` and `type` ' +
214
+ 'refuse it rather than write the DOM and submit the old content. A contenteditable editor ' +
215
+ '(Tiptap, ProseMirror) takes `insertText` with `{ text }`: it goes through the browser editing ' +
216
+ 'pipeline, so the editor model updates. An EditContext editor (Monaco, CodeMirror 6) cannot be ' +
217
+ 'typed into yet, because a scripted update fires no textupdate for its model: use `press` for ' +
218
+ 'a key. If the ref points at a wrapper, act on the editor inside it instead.',
208
219
  HOVER_NEEDS_POINTER: 'Hover needs a real pointer: a synthetic mouseover does not apply CSS :hover, and Reticle ' +
209
220
  'refuses rather than reporting the styles as applied. Acquire a tab with ' +
210
221
  'reticle_run { tool: "reticle_lease", args: { action: "acquire", url } } — reticle_lease is not ' +
@@ -256,7 +267,7 @@ export const RECOVERY = {
256
267
  'what is wrong with the path). The call itself was fine, so retrying it will not help, and ' +
257
268
  'neither will installing Chromium. Ask the human to fix that path, or unset it to use ' +
258
269
  "Playwright's own Chromium, in the environment the daemon starts from, then run " +
259
- '`npx @reticlehq/server restart`. Meanwhile drive a tab the human already has open, which ' +
270
+ '`npx @reticlehq/server serve --restart`. Meanwhile drive a tab the human already has open, which ' +
260
271
  'reticle_sessions lists. This is configuration, not a Reticle defect: there is nothing to report.',
261
272
  /**
262
273
  * The two ways a platform answer can refuse a drive. Neither is a fault, and both were telling the
@@ -322,6 +333,44 @@ const REASON_OF = {
322
333
  // The call is valid; the app is not instrumented enough for the Harness yet.
323
334
  HARNESS_LOCKED: RefusalReason.UNSUPPORTED,
324
335
  };
336
+ /**
337
+ * Which CAUSE each recovery names, for `tool_refused`. A `Record` for the same reason as `REASON_OF`:
338
+ * a recovery added without a detail does not compile. A rule can name a narrower detail than its
339
+ * recovery (`NO_POOL` covers three causes with one hint).
340
+ */
341
+ const DETAIL_OF = {
342
+ NO_SESSION: RefusalDetail.NO_SESSION,
343
+ MULTIPLE_SESSIONS: RefusalDetail.MULTIPLE_SESSIONS,
344
+ UNKNOWN_SESSION: RefusalDetail.UNKNOWN_SESSION,
345
+ SCOPE_MISMATCH: RefusalDetail.SCOPE_MISMATCH,
346
+ SESSION_GONE: RefusalDetail.SESSION_GONE,
347
+ THROTTLED: RefusalDetail.THROTTLED,
348
+ COMMAND_TIMEOUT: RefusalDetail.COMMAND_TIMEOUT,
349
+ NO_POOL: RefusalDetail.POOL_UNAVAILABLE,
350
+ CHROMIUM_PATH: RefusalDetail.CHROMIUM_PATH,
351
+ TOKEN_REQUIRED: RefusalDetail.TOKEN_REQUIRED,
352
+ MISSING_BASELINE: RefusalDetail.MISSING_BASELINE,
353
+ MISSING_RECORDING: RefusalDetail.MISSING_RECORDING,
354
+ STALE_REF: RefusalDetail.STALE_REF,
355
+ STALE_REF_AFTER_EDIT: RefusalDetail.STALE_REF_AFTER_EDIT,
356
+ UNISSUED_REF: RefusalDetail.UNISSUED_REF,
357
+ NO_SUCH_OPTION: RefusalDetail.NO_SUCH_OPTION,
358
+ TARGET_MISSED: RefusalDetail.TARGET_MISSED,
359
+ AMBIGUOUS_TARGET: RefusalDetail.AMBIGUOUS_TARGET,
360
+ FLOW_STEP_MISSING: RefusalDetail.FLOW_STEP_MISSING,
361
+ UNSUPPORTED_SURFACE: RefusalDetail.UNSUPPORTED_SURFACE,
362
+ HOVER_NEEDS_POINTER: RefusalDetail.HOVER_NEEDS_POINTER,
363
+ NOT_EDITABLE: RefusalDetail.NOT_EDITABLE,
364
+ CONFIRM_DANGEROUS: RefusalDetail.CONFIRM_DANGEROUS,
365
+ WRONG_TARGET: RefusalDetail.WRONG_TARGET,
366
+ PRODUCTION_POLICY: RefusalDetail.PRODUCTION_POLICY,
367
+ BAD_ARGUMENTS: RefusalDetail.BAD_ARGUMENTS,
368
+ INVALID_NAME: RefusalDetail.INVALID_NAME,
369
+ HARNESS_OFF: RefusalDetail.HARNESS_OFF,
370
+ HARNESS_LOCKED: RefusalDetail.HARNESS_LOCKED,
371
+ };
372
+ /** Hint text back to its detail, as `REASON_BY_HINT` does for the reason. */
373
+ const DETAIL_BY_HINT = new Map(Object.entries(RECOVERY).map(([key, hint]) => [hint, DETAIL_OF[key]]));
325
374
  /** Hint text back to its reason. The hints are distinct strings, so this inverts cleanly. */
326
375
  const REASON_BY_HINT = new Map(Object.entries(RECOVERY).map(([key, hint]) => [hint, REASON_OF[key]]));
327
376
  /** Ordered match rules; the first hit wins. Substrings track the thrown messages they recover. */
@@ -372,9 +421,17 @@ const RULES = [
372
421
  { match: /^command '[^']*' timed out after/i, hint: RECOVERY.COMMAND_TIMEOUT },
373
422
  // Flow replay: the recorded anchor is gone. Ordinary drift, and the tool that fixes it exists.
374
423
  { match: /did not resolve in current page/i, hint: RECOVERY.FLOW_STEP_MISSING },
424
+ // One hint for three causes: the agent's next step is the same, the fix for the user is not.
425
+ { match: /browser pool unavailable/i, hint: RECOVERY.NO_POOL },
426
+ {
427
+ match: /browser pool is shut down/i,
428
+ hint: RECOVERY.NO_POOL,
429
+ detail: RefusalDetail.POOL_SHUT_DOWN,
430
+ },
375
431
  {
376
- match: /browser pool (?:unavailable|is shut down)|Chromium is not installed/i,
432
+ match: /Chromium is not installed/i,
377
433
  hint: RECOVERY.NO_POOL,
434
+ detail: RefusalDetail.CHROMIUM_MISSING,
378
435
  },
379
436
  // The commonest post-action condition there is. Unmatched, it fell through to FEEDBACK_ASK and
380
437
  // told the agent a successful click's aftermath might be a bug in Reticle.
@@ -389,6 +446,10 @@ const RULES = [
389
446
  // broken. Order matters: contenteditable and disabled/readonly are `cannot <verb> …` messages too,
390
447
  // so they must be tested before the general wrong-target rule.
391
448
  { match: /contenteditable/i, hint: RECOVERY.UNSUPPORTED_SURFACE },
449
+ // The EditContext refusal from insertText. Its message names no `contenteditable`, so it used to
450
+ // fall through to the feedback ask — a deliberate refusal of a surface we cannot type into yet
451
+ // read as "Reticle may be broken".
452
+ { match: /EditContext/i, hint: RECOVERY.UNSUPPORTED_SURFACE },
392
453
  { match: /cannot hover without a real pointer/i, hint: RECOVERY.HOVER_NEEDS_POINTER },
393
454
  { match: /cannot \w+ a (disabled|readonly) </i, hint: RECOVERY.NOT_EDITABLE },
394
455
  // Both "action" and "native action" refusals need the same confirmation guidance.
@@ -464,15 +525,17 @@ const RULES = [
464
525
  * saying a server is running and the recovery saying to go start one.
465
526
  */
466
527
  export function recoveryFor(message) {
467
- if (isUnissuedRefRefusal(message))
528
+ if (isUnissuedRefRefusal(message)) {
468
529
  return RECOVERY.UNISSUED_REF;
469
- if (isSelfRecovering(message))
530
+ }
531
+ if (isSelfRecovering(message)) {
470
532
  return undefined;
471
- for (const rule of RULES) {
472
- if (rule.match.test(message))
473
- return rule.hint;
474
533
  }
475
- return undefined;
534
+ return firstRule(message)?.hint;
535
+ }
536
+ /** The first rule that matches the message. The order of `RULES` decides. */
537
+ function firstRule(message) {
538
+ return RULES.find((rule) => rule.match.test(message));
476
539
  }
477
540
  /**
478
541
  * WHY a tool refused, as one of the six buckets in core's `RefusalReason`.
@@ -483,27 +546,46 @@ export function recoveryFor(message) {
483
546
  * this table rather than about anybody's app.
484
547
  */
485
548
  export function refusalReasonFor(rawMessage, coded) {
486
- if (coded !== undefined)
487
- return REASON_OF_CODE[coded.code];
549
+ return classifyRefusal(rawMessage, coded).reason;
550
+ }
551
+ /**
552
+ * The bucket and, when the recovery table names one, the cause inside it. The cause comes from the
553
+ * same rule that chose the agent's advice, so the two cannot disagree. No detail means no rule
554
+ * matched, or the message diagnosed itself (`noSessionReason` carries that cause).
555
+ */
556
+ export function classifyRefusal(rawMessage, coded) {
557
+ if (coded !== undefined) {
558
+ return { reason: REASON_OF_CODE[coded.code] };
559
+ }
488
560
  const message = zodArrayAsSentence(rawMessage);
489
- if (isUnissuedRefRefusal(message))
490
- return RefusalReason.BAD_ARGS;
561
+ if (isUnissuedRefRefusal(message)) {
562
+ return { reason: RefusalReason.BAD_ARGS, detail: RefusalDetail.UNISSUED_REF };
563
+ }
491
564
  // The no-session diagnosis inspected the machine and named the cause itself, so it never reaches
492
565
  // the recovery table. It is the single largest refusal there is; missing it would gut the metric.
493
- if (message.includes(SELF_RECOVERING_MARKER))
494
- return RefusalReason.NO_SESSION;
566
+ if (message.includes(SELF_RECOVERING_MARKER)) {
567
+ return { reason: RefusalReason.NO_SESSION };
568
+ }
495
569
  for (const rule of SELF_RECOVERING) {
496
- if (rule.match.test(message))
497
- return rule.reason;
570
+ if (rule.match.test(message)) {
571
+ return { reason: rule.reason };
572
+ }
498
573
  }
499
574
  // Same order as buildErrorPayload: a schema rejection is the agent's own call, whatever else in
500
575
  // the message might also match.
501
- if (ARGUMENT_REJECTION.test(message))
502
- return RefusalReason.BAD_ARGS;
503
- if (INVALID_NAME_REJECTION.test(message))
504
- return RefusalReason.BAD_ARGS;
505
- const hint = recoveryFor(message);
506
- return (hint === undefined ? undefined : REASON_BY_HINT.get(hint)) ?? RefusalReason.OTHER;
576
+ if (ARGUMENT_REJECTION.test(message)) {
577
+ return { reason: RefusalReason.BAD_ARGS };
578
+ }
579
+ if (INVALID_NAME_REJECTION.test(message)) {
580
+ return { reason: RefusalReason.BAD_ARGS, detail: RefusalDetail.INVALID_NAME };
581
+ }
582
+ const rule = firstRule(message);
583
+ if (rule === undefined) {
584
+ return { reason: RefusalReason.OTHER };
585
+ }
586
+ const reason = REASON_BY_HINT.get(rule.hint) ?? RefusalReason.OTHER;
587
+ const detail = rule.detail ?? DETAIL_BY_HINT.get(rule.hint);
588
+ return detail === undefined ? { reason } : { reason, detail };
507
589
  }
508
590
  /**
509
591
  * A schema rejection: the call named a parameter that does not exist, or gave one the wrong type.
@@ -520,8 +602,9 @@ export function refusalReasonFor(rawMessage, coded) {
520
602
  /** Max issues rendered — a union rejection can emit one per member, which is how the array became unreadable. */
521
603
  const MAX_RENDERED_ISSUES = 3;
522
604
  function isZodIssue(value) {
523
- if (typeof value !== 'object' || null === value)
605
+ if (typeof value !== 'object' || null === value) {
524
606
  return false;
607
+ }
525
608
  const v = value;
526
609
  return 'string' === typeof v['code'] && 'string' === typeof v['message'];
527
610
  }
@@ -548,8 +631,9 @@ function issueClause(issue) {
548
631
  */
549
632
  function zodArrayAsSentence(message) {
550
633
  const text = message.trim();
551
- if (!text.startsWith('['))
634
+ if (!text.startsWith('[')) {
552
635
  return message;
636
+ }
553
637
  let parsed;
554
638
  try {
555
639
  parsed = JSON.parse(text);
@@ -557,8 +641,9 @@ function zodArrayAsSentence(message) {
557
641
  catch {
558
642
  return message; // not JSON at all — e.g. prose that happens to open with a bracket
559
643
  }
560
- if (!Array.isArray(parsed) || 0 === parsed.length || !parsed.every(isZodIssue))
644
+ if (!Array.isArray(parsed) || 0 === parsed.length || !parsed.every(isZodIssue)) {
561
645
  return message;
646
+ }
562
647
  const shown = parsed.slice(0, MAX_RENDERED_ISSUES).map(issueClause).join('; ');
563
648
  const rest = parsed.length - MAX_RENDERED_ISSUES;
564
649
  return rest > 0 ? `${shown} (and ${String(rest)} more)` : shown;
@@ -613,8 +698,9 @@ const REASON_OF_CODE = {
613
698
  * its sentence: the not-linked refusal was matched by its prose, and the prose was rewritten.
614
699
  */
615
700
  export function codedRefusalOf(error) {
616
- if (!(error instanceof Error))
701
+ if (!(error instanceof Error)) {
617
702
  return undefined;
703
+ }
618
704
  const parsed = CodedRefusalSchema.safeParse({
619
705
  code: 'code' in error ? error.code : undefined,
620
706
  next: 'next' in error ? error.next : undefined,
@@ -631,24 +717,29 @@ export function buildErrorPayload(rawMessage, coded) {
631
717
  // leave an unparseable fragment as the agent's error text — the worst of both shapes.
632
718
  const message = capMessage(zodArrayAsSentence(rawMessage));
633
719
  // A coded refusal says who does what next itself: no hint beside it, and no bug-report ask.
634
- if (coded !== undefined)
720
+ if (coded !== undefined) {
635
721
  return { error: message, code: coded.code, next: coded.next };
636
- if (isUnissuedRefRefusal(message))
722
+ }
723
+ if (isUnissuedRefRefusal(message)) {
637
724
  return { error: message, recovery: RECOVERY.UNISSUED_REF };
725
+ }
638
726
  // A self-diagnosing message gets NEITHER a generic recovery nor the feedback ask. It already
639
727
  // inspected the machine and named the cause; a second, contradictory hint is noise, and inviting a
640
728
  // bug report about a condition Reticle diagnosed itself is exactly backwards.
641
- if (isSelfRecovering(message))
729
+ if (isSelfRecovering(message)) {
642
730
  return { error: message };
731
+ }
643
732
  // An argument rejection is the agent's to fix, not a Reticle defect. Reported from the field: a
644
733
  // clean `unrecognized_keys: ["value"]` came back wrapped in "may be a defect in Reticle", which
645
734
  // spends the agent's turn and fills the feedback channel with reports about malformed calls.
646
- if (ARGUMENT_REJECTION.test(message))
735
+ if (ARGUMENT_REJECTION.test(message)) {
647
736
  return { error: message, recovery: ARGUMENT_RECOVERY };
737
+ }
648
738
  // Reticle's own name validators rejecting a caller's value: an argument rejection that the schema
649
739
  // cannot express (the shape is a runtime pattern, not a type), so it needs its own arm.
650
- if (INVALID_NAME_REJECTION.test(message))
740
+ if (INVALID_NAME_REJECTION.test(message)) {
651
741
  return { error: message, recovery: RECOVERY.INVALID_NAME };
742
+ }
652
743
  const recovery = recoveryFor(message);
653
744
  return recovery !== undefined
654
745
  ? { error: message, recovery }
@@ -60,7 +60,7 @@ export const EXPLORE_TOOLS = [
60
60
  runId: z
61
61
  .string()
62
62
  .optional()
63
- .describe('The run id an explore call answered with; pass it to poll that run.'),
63
+ .describe('The run id a call answered with; pass it to poll that run.'),
64
64
  planId: z.string().optional().describe('Run a plan saved on the dashboard, as it stands.'),
65
65
  stop: z.boolean().optional().describe('With runId: stop that Harness run.'),
66
66
  say: z.string().optional().describe('With runId: a note to the running Harness.'),
@@ -17,6 +17,7 @@ import { endsRun, noteToolResult, runReportField } from './run-report.js';
17
17
  import { runLedger } from '../../judgement/runs/run-ledger.js';
18
18
  import { readAppNotes } from './agent/app-notes.js';
19
19
  import { forTheAgent } from './agent/for-the-agent.js';
20
+ import { inRepeatStreak, withHintOn } from './repeat-hint.js';
20
21
  import { feedbackAlreadyAsked, noteFeedbackAsked } from './agent/notices.js';
21
22
  import { SKEW_SHORT_WARNING, SKEW_VERDICT_WARNING } from '../../portal/session/session-health.js';
22
23
  import { verifyNextBaton, SUPPRESS_VERIFY_NEXT_ENV } from './verify-next-baton.js';
@@ -29,7 +30,7 @@ import { rewriteClosedAsSkew } from '../../command/version/version-skew.js';
29
30
  import { noteToolCall } from '../../command/daemon/lifetime/daemon-usefulness.js';
30
31
  import { bugsInResult, routeOf } from '../../telemetry/bug-found.js';
31
32
  import { noteToolServed, reportToolRefused } from '../../telemetry/tool-refused.js';
32
- import { buildErrorPayload, codedRefusalOf, refusalReasonFor } from './error-recovery.js';
33
+ import { buildErrorPayload, classifyRefusal, codedRefusalOf } from './error-recovery.js';
33
34
  import { resultIsError } from '../mcp/faults/mcp-is-error.js';
34
35
  import { verificationOf } from '../../telemetry/verification-of.js';
35
36
  import { emitBugFoundHook, emitVerdictHook } from '../../hooks/hook-emit.js';
@@ -46,6 +47,7 @@ import { takeStepShot } from '../../features/visual/step-shots.js';
46
47
  import { defectForToolResult, deltaForToolResult, impactSnapshot, initImpact, recordImpact, } from '../../memory/impact/impact-recorder.js';
47
48
  import { FrictionKind, frictionOf, inviteFor } from './feedback-invite.js';
48
49
  import { VACUOUS_BECAUSE } from '@reticlehq/engine/evidence/verified.js';
50
+ import { NEXT_STEP_FIELD } from './empty-session-list.js';
49
51
  /**
50
52
  * Emit `verification_completed` when a verification tool produced a verdict.
51
53
  *
@@ -134,11 +136,12 @@ function reportBugsFound(toolName, result, sessionId) {
134
136
  * tool's name, and then hands the same failure back out to be reported a second time as
135
137
  * `reticle_run`. A count that doubles because of HOW a tool was reached is not a measurement.
136
138
  */
137
- function reportRefusal(toolName, message, error) {
139
+ function reportRefusal(toolName, args, message, error) {
138
140
  if (toolName === ReticleTool.RUN) {
139
141
  return;
140
142
  }
141
- reportToolRefused(toolName, refusalReasonFor(message, codedRefusalOf(error)));
143
+ const { reason, detail } = classifyRefusal(message, codedRefusalOf(error));
144
+ reportToolRefused(toolName, reason, args, detail);
142
145
  }
143
146
  /**
144
147
  * Which friction this result represents, counting the invitation if there is one.
@@ -256,25 +259,20 @@ export async function runTool(tool, deps, given, call) {
256
259
  // The project of the session a call ends, read while the session still exists: releasing a
257
260
  // tab removes it, and an unreadable project lost an unlinked user's cloud line.
258
261
  const endedIn = agentCall && ends ? safeRoot(() => sessionRoot(deps, sessionIdFromArgs(args))) : undefined;
259
- const raw = await loggedTool(tool, deps, args, call);
262
+ // `reticle_run` is counted once, as the tool it reached; never another agent's call (rule 8).
263
+ const owner = currentDrivenBy() === undefined && ReticleTool.RUN !== tool.name;
264
+ const { raw, hint } = await inRepeatStreak(owner ? deps.attachId : undefined, tool.name, args, () => loggedTool(tool, deps, args, call));
260
265
  const attach = currentDrivenBy() === undefined ? deps.attachId : undefined;
261
266
  const root = attach === undefined
262
267
  ? undefined
263
268
  : safeRoot(() => sessionRoot(deps, sessionIdFromArgs(args)));
264
269
  // The app notes before this call, so a note it learns counts as memory written.
265
270
  const notesBefore = agentCall && root !== undefined ? readAppNotes(root) : [];
266
- const answer = forTheAgent({
267
- name: tool.name,
268
- args,
269
- result: raw,
270
- attach,
271
- root,
272
- fold: NOTICE_FOLD,
273
- });
271
+ const answer = withHintOn(forTheAgent({ name: tool.name, args, result: raw, attach, root, fold: NOTICE_FOLD }), hint);
274
272
  if (!agentCall) {
275
273
  return answer;
276
274
  }
277
- runLedger.after(deps.now());
275
+ runLedger.after(deps.now(), deps.attachId);
278
276
  noteToolResult(runLedger, tool.name, args, raw);
279
277
  const notesAfter = root === undefined ? [] : readAppNotes(root);
280
278
  runLedger.memoryWritten(notesAfter.filter((note) => !notesBefore.includes(note)).length);
@@ -282,7 +280,10 @@ export async function runTool(tool, deps, given, call) {
282
280
  if (!ends || null === answer || 'object' !== typeof answer) {
283
281
  return answer;
284
282
  }
285
- return { ...answer, ...runReportField(runLedger, endedIn ?? root, process.env, deps.now()) };
283
+ return {
284
+ ...answer,
285
+ ...runReportField(runLedger, endedIn ?? root, process.env, deps.now(), deps.attachId),
286
+ };
286
287
  }));
287
288
  }
288
289
  /** Advisories that become once-per-session notice lines. See agent/notices.ts. */
@@ -391,7 +392,7 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
391
392
  'value — a selector or an id rather than a document, or a slice of the text you need.';
392
393
  // Refused before the handler was ever entered, and still a refusal the agent has to recover
393
394
  // from. Reported here rather than at the return below, which this branch never reaches.
394
- reportRefusal(tool.name, error);
395
+ reportRefusal(tool.name, args, error);
395
396
  return { error };
396
397
  }
397
398
  // One driver per tab. The Harness clicking through a page while the agent clicks too is two hands
@@ -399,7 +400,7 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
399
400
  const busy = ACTION_TOOLS.has(tool.name) ? harnessDriving(deps, args) : undefined;
400
401
  if (busy !== undefined) {
401
402
  const error = msgHarnessHasTheTab(busy);
402
- reportRefusal(tool.name, error);
403
+ reportRefusal(tool.name, args, error);
403
404
  return { error };
404
405
  }
405
406
  // An ACTION is what gets abandoned. Counted here, against verifications, so "the agent drove the
@@ -448,7 +449,7 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
448
449
  // before Playwright invents a closed page. ready:true leases still hit this path.
449
450
  if (session?.versionSkew !== undefined && CDP_TOOLS.has(tool.name)) {
450
451
  const message = session.versionSkew;
451
- reportRefusal(tool.name, message);
452
+ reportRefusal(tool.name, args, message);
452
453
  throw new Error(message);
453
454
  }
454
455
  // A ref that was handed out by a DIFFERENT tab, on a call that named no session. Refused here, at
@@ -466,7 +467,7 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
466
467
  if (wrongTab !== undefined) {
467
468
  // Refused before the handler was entered, so it is reported here — the same shape as the
468
469
  // oversized-argument refusal above.
469
- reportRefusal(tool.name, wrongTab);
470
+ reportRefusal(tool.name, args, wrongTab);
470
471
  return { error: wrongTab };
471
472
  }
472
473
  }
@@ -516,7 +517,7 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
516
517
  const rewritten = rewriteClosedAsSkew(error, skewText);
517
518
  const toThrow = rewritten ?? error;
518
519
  const message = toThrow instanceof Error ? toThrow.message : String(toThrow);
519
- reportRefusal(tool.name, message, toThrow);
520
+ reportRefusal(tool.name, args, message, toThrow);
520
521
  // The invitation on THIS path is the one `buildErrorPayload` attaches when it does not recognise
521
522
  // the error, and it was never counted — so the denominator excluded the commonest friction there
522
523
  // is. Gated on the payload actually carrying the ask, because a recognised error gets a recovery
@@ -580,7 +581,7 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
580
581
  // project the daemon was started in.
581
582
  session?.pushImpact?.(() => impactSnapshot(artifactRoot));
582
583
  if (resultIsError(raw)) {
583
- reportRefusal(tool.name, raw.error);
584
+ reportRefusal(tool.name, args, raw.error);
584
585
  }
585
586
  else {
586
587
  noteToolServed();
@@ -660,6 +661,11 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
660
661
  })
661
662
  : undefined;
662
663
  const platformLine = platform === undefined ? nudge : nudge === undefined ? platform : `${platform}\n${nudge}`;
664
+ // The first call of this connection, in an unwired app with nothing attached: the offer the
665
+ // plugin's SessionStart hook makes, which `/reload-plugins` never fires. Always taken, so a call
666
+ // that already said it (the opened tab's notice, an empty list's `next_step`) still uses the once.
667
+ const firstOffer = agentCall && deps.attachId !== undefined ? deps.firstSessionOffer?.(deps.attachId) : undefined;
668
+ const offer = opened === undefined && !(NEXT_STEP_FIELD in (answer ?? {})) ? firstOffer : undefined;
663
669
  const drives = agentCall
664
670
  ? takeDriveNotes('string' === typeof answer?.['runId'] ? answer['runId'] : undefined)
665
671
  : [];
@@ -675,7 +681,8 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
675
681
  undelivered === undefined &&
676
682
  friction === undefined &&
677
683
  platformLine === undefined &&
678
- next === undefined
684
+ next === undefined &&
685
+ offer === undefined
679
686
  ? raw
680
687
  : {
681
688
  // First, so a model that truncates a long result still reads it.
@@ -696,6 +703,7 @@ async function dispatchTool(tool, deps, args, call, seen = {}) {
696
703
  ...(update !== undefined ? { [EnvelopeKey.UPDATE_AVAILABLE]: update } : {}),
697
704
  ...(skew !== undefined ? { [EnvelopeKey.VERSION_SKEW]: skew } : {}),
698
705
  ...(platformLine !== undefined ? { [EnvelopeKey.PLATFORM]: platformLine } : {}),
706
+ ...(offer === undefined ? {} : { [EnvelopeKey.FIRST_SESSION_OFFER]: offer }),
699
707
  ...(0 === drives.length ? {} : { [EnvelopeKey.HARNESS]: drives.join('\n') }),
700
708
  ...(notes === undefined ? {} : { [EnvelopeKey.CONTROL]: notes }),
701
709
  ...(undelivered !== undefined
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `reticle_lease { action: "coverage" }` — which code of a leased page ran, as the JS engine counted it.
3
+ *
4
+ * A caller outside the app cannot see which branches a drive reached. The JS engine can: each script's block
5
+ * ranges carry a count. This reads them from the lease's own Playwright page, the way
6
+ * `reticle_network_mock` reaches it, so it needs no change to the app.
7
+ *
8
+ * A take returns what ran since the previous take, and coverage keeps running across a reload.
9
+ * Start it on acquire (`jsCoverage: true`) to see the code that runs while the page loads; a start
10
+ * after the load misses it.
11
+ */
12
+ import { z } from 'zod';
13
+ import type { ScriptCoverage } from '../../portal/input/js-coverage.js';
14
+ import { type ToolDef } from './tool-kit.js';
15
+ export declare const LeaseCoverageMode: {
16
+ readonly START: "start";
17
+ readonly TAKE: "take";
18
+ };
19
+ /** Ranges one take returns when the caller sets no cap. */
20
+ export declare const DEFAULT_MAX_COVERAGE_RANGES = 20000;
21
+ interface Range {
22
+ startOffset: number;
23
+ endOffset: number;
24
+ count: number;
25
+ }
26
+ /** What a take returns: each kept script with its ranges, and what the cap and filter left out. */
27
+ export interface CoverageTake {
28
+ scripts: {
29
+ url: string;
30
+ ranges: Range[];
31
+ }[];
32
+ scriptCount: number;
33
+ rangeCount: number;
34
+ filteredOut?: number;
35
+ truncated?: {
36
+ scripts: number;
37
+ ranges: number;
38
+ maxRanges: number;
39
+ };
40
+ }
41
+ /**
42
+ * Flatten each script's functions into one range list, keep the scripts `urlContains` names, and
43
+ * stop at `maxRanges`. What the cap drops is counted in `truncated`, beside the value.
44
+ */
45
+ export declare function flattenCoverage(entries: readonly ScriptCoverage[], maxRanges: number, urlContains?: string): CoverageTake;
46
+ /** The acquire argument that starts coverage before the page's first script. */
47
+ export declare const LEASE_JS_COVERAGE_ARG: z.ZodOptional<z.ZodBoolean>;
48
+ export declare const LEASE_COVERAGE_TOOL: ToolDef;
49
+ export {};