@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
@@ -10,6 +10,10 @@ export declare const REDUCED_MOTION_ARG: z.ZodOptional<z.ZodEnum<{
10
10
  reduce: "reduce";
11
11
  "no-preference": "no-preference";
12
12
  }>>;
13
+ export declare const POINTER_ARG: z.ZodOptional<z.ZodEnum<{
14
+ fine: "fine";
15
+ coarse: "coarse";
16
+ }>>;
13
17
  export declare const LEASE_EMULATE_ARG: z.ZodOptional<z.ZodObject<{
14
18
  colorScheme: z.ZodOptional<z.ZodEnum<{
15
19
  light: "light";
@@ -22,6 +26,10 @@ export declare const LEASE_EMULATE_ARG: z.ZodOptional<z.ZodObject<{
22
26
  }>>;
23
27
  width: z.ZodOptional<z.ZodNumber>;
24
28
  height: z.ZodOptional<z.ZodNumber>;
29
+ pointer: z.ZodOptional<z.ZodEnum<{
30
+ fine: "fine";
31
+ coarse: "coarse";
32
+ }>>;
25
33
  }, z.core.$strict>>;
26
34
  /** The media part of what a caller asked for, or undefined when it asked for none. */
27
35
  export declare function mediaOf(raw: {
@@ -4,11 +4,12 @@ import { ReticleTool } from '@reticlehq/core';
4
4
  import { sessionIdShape } from '../../surface/tools/tool-kit.js';
5
5
  import { MAX_VIEWPORT_PX, MIN_VIEWPORT_PX, viewportPxSchema, } from '../../surface/tools/args/numeric-bounds.js';
6
6
  import { asString } from '@reticlehq/core';
7
- import { ColorScheme, ReducedMotion, } from '../pool/lease-emulation.js';
7
+ import { HEADLESS_VIEWPORT } from '../../chromium-launch-options.js';
8
+ import { ColorScheme, Pointer, ReducedMotion, } from '../pool/lease-emulation.js';
8
9
  // ── Media and width, as a tool call names them (reticle_viewport here, reticle_lease acquire) ──
9
- /** Playwright's own default page size, used for the side a caller did not name. */
10
- const DEFAULT_WIDTH = 1280;
11
- const DEFAULT_HEIGHT = 720;
10
+ /** The side a caller did not name is the page size a headless lease already opens with. */
11
+ const DEFAULT_WIDTH = HEADLESS_VIEWPORT.width;
12
+ const DEFAULT_HEIGHT = HEADLESS_VIEWPORT.height;
12
13
  export const COLOR_SCHEME_ARG = z
13
14
  .enum([ColorScheme.LIGHT, ColorScheme.DARK, ColorScheme.NO_PREFERENCE])
14
15
  .optional()
@@ -17,16 +18,24 @@ export const REDUCED_MOTION_ARG = z
17
18
  .enum([ReducedMotion.REDUCE, ReducedMotion.NO_PREFERENCE])
18
19
  .optional()
19
20
  .describe('prefers-reduced-motion for this page: reduce or no-preference.');
21
+ export const POINTER_ARG = z
22
+ .enum([Pointer.FINE, Pointer.COARSE])
23
+ .optional()
24
+ .describe('The primary pointer this page reports to @media (pointer: ...): fine (a mouse) or coarse (touch). ' +
25
+ 'Settable only when the lease opens, because touch support is fixed for a browser context.');
20
26
  const EmulateSchema = z
21
27
  .object({
22
28
  colorScheme: COLOR_SCHEME_ARG,
23
29
  reducedMotion: REDUCED_MOTION_ARG,
24
30
  width: viewportPxSchema.optional(),
25
31
  height: viewportPxSchema.optional(),
32
+ pointer: POINTER_ARG,
26
33
  })
27
34
  .strict();
28
- export const LEASE_EMULATE_ARG = EmulateSchema.optional().describe('Open the lease with these set before the first paint: { colorScheme, reducedMotion, width, height }. ' +
29
- 'A side you leave out keeps its default (1280 by 720). Change them later with reticle_viewport.');
35
+ export const LEASE_EMULATE_ARG = EmulateSchema.optional().describe('Open the lease with these set before the first paint: { colorScheme, reducedMotion, width, height, pointer }. ' +
36
+ 'A side you leave out keeps its default (1440 by 900). Change the media and the size later with ' +
37
+ 'reticle_viewport; `pointer` cannot change on an open lease, so a touch-only rule needs a lease ' +
38
+ 'acquired with pointer: "coarse".');
30
39
  /** The media part of what a caller asked for, or undefined when it asked for none. */
31
40
  export function mediaOf(raw) {
32
41
  const parsed = EmulateSchema.pick({ colorScheme: true, reducedMotion: true }).parse({
@@ -41,21 +50,27 @@ export function mediaOf(raw) {
41
50
  }
42
51
  /** A lease's `emulate` argument, read; a value it does not know is refused by name. */
43
52
  export function leaseEmulationOf(raw) {
44
- if (raw === undefined)
53
+ if (raw === undefined) {
45
54
  return undefined;
55
+ }
46
56
  const parsed = EmulateSchema.safeParse(raw);
47
57
  if (!parsed.success) {
48
58
  const field = parsed.error.issues[0]?.path.join('.') ?? 'emulate';
49
59
  throw new Error(`emulate.${field}: ${parsed.error.issues[0]?.message ?? 'not valid'}`);
50
60
  }
51
- const { width, height } = parsed.data;
61
+ const { width, height, pointer } = parsed.data;
52
62
  const media = mediaOf(parsed.data);
53
63
  const viewport = width === undefined && height === undefined
54
64
  ? undefined
55
65
  : { width: width ?? DEFAULT_WIDTH, height: height ?? DEFAULT_HEIGHT };
56
- if (media === undefined && viewport === undefined)
66
+ if (media === undefined && viewport === undefined && pointer === undefined) {
57
67
  return undefined;
58
- return { ...media, ...(viewport === undefined ? {} : { viewport }) };
68
+ }
69
+ return {
70
+ ...media,
71
+ ...(viewport === undefined ? {} : { viewport }),
72
+ ...(pointer === undefined ? {} : { pointer }),
73
+ };
59
74
  }
60
75
  /** Bounds so a viewport request stays sane (and a typo can't ask for a 1px or 100k-px window). */
61
76
  const MIN_DIM = MIN_VIEWPORT_PX;
@@ -66,6 +81,25 @@ function viewportProvider(deps) {
66
81
  ? p
67
82
  : undefined;
68
83
  }
84
+ /**
85
+ * Whether this caller holds the session as a lease. Another agent's lease is never this caller's
86
+ * to change (#1226).
87
+ */
88
+ function holdsLease(deps, sessionId) {
89
+ return true === deps.pool?.heldBy(sessionId, deps.attachId);
90
+ }
91
+ /** Whether this call can resize the session: a lease of this caller, or a driven page. */
92
+ async function canResize(deps, session) {
93
+ if (holdsLease(deps, session.id)) {
94
+ return true;
95
+ }
96
+ // A driven browser resizes only a page it has. Having a driven browser is not enough.
97
+ const driven = viewportProvider(deps);
98
+ if (driven === undefined) {
99
+ return false;
100
+ }
101
+ return driven.isAvailableFor(session.url);
102
+ }
69
103
  export const VIEWPORT_TOOLS = [
70
104
  {
71
105
  name: ReticleTool.VIEWPORT,
@@ -73,37 +107,55 @@ export const VIEWPORT_TOOLS = [
73
107
  'reproducible across machines — the missing piece of CI-stable visual regression, alongside ' +
74
108
  'reticle_visual_diff `masks` and a frozen clock (reticle_clock). Set it once before reticle_screenshot / ' +
75
109
  'reticle_visual_diff. On a lease it also sets prefers-color-scheme and prefers-reduced-motion; ' +
76
- 'give only those to change the media and keep the size. Returns { applied, width, height, observed } or the no-provider recommendation; `observed` is the size the leased page reports after the resize.',
110
+ 'give only those to change the media and keep the size. A `pointer` is refused here: touch support is fixed when a lease opens, so ask for it with reticle_lease acquire `emulate.pointer`. ' +
111
+ 'Returns { applied, width, height, observed } or the no-provider recommendation; `observed` is the size the leased page reports after the resize.',
77
112
  inputSchema: {
78
113
  width: viewportPxSchema.optional().describe('Viewport width in CSS px (e.g. 1280).'),
79
114
  height: viewportPxSchema.optional().describe('Viewport height in CSS px (e.g. 800).'),
80
115
  colorScheme: COLOR_SCHEME_ARG,
81
116
  reducedMotion: REDUCED_MOTION_ARG,
117
+ pointer: POINTER_ARG,
82
118
  ...sessionIdShape,
83
119
  },
84
120
  outputSchema: {
85
121
  applied: z.boolean(),
86
- width: z.number(),
87
- height: z.number(),
122
+ width: z.number().optional(),
123
+ height: z.number().optional(),
88
124
  observed: z.object({ width: z.number(), height: z.number() }).optional(),
89
125
  colorScheme: z.string().optional(),
90
126
  reducedMotion: z.string().optional(),
127
+ pointer: z.string().optional(),
91
128
  ok: z.boolean().optional(),
92
129
  reason: z.string().optional(),
93
130
  recommendation: z.string().optional(),
94
131
  },
95
132
  handler: async (deps, args) => {
133
+ // Touch support is a Playwright context option and a lease is one context, so a pointer can
134
+ // only be chosen when the lease opens. Said out loud instead of dropped: a silently ignored
135
+ // `pointer` would hand back a verdict about a touch-only rule that was taken with a mouse.
136
+ if (args['pointer'] !== undefined) {
137
+ return {
138
+ applied: false,
139
+ ok: false,
140
+ reason: POINTER_IS_FIXED,
141
+ recommendation: POINTER_NEEDS_A_NEW_LEASE,
142
+ };
143
+ }
96
144
  const media = mediaOf(args);
97
145
  if (media !== undefined && args['width'] === undefined && args['height'] === undefined) {
98
- const session = deps.sessions.resolve(asString(args['sessionId']));
146
+ // Only a lease can set the media.
147
+ const session = await deps.sessions.resolveServing(asString(args['sessionId']), (s) => holdsLease(deps, s.id));
99
148
  if (true === (await deps.pool?.emulateLease(session.id, media))) {
100
- return { applied: true, width: 0, height: 0, ...media };
149
+ // No size was asked for, so answer the one the lease has; 0 by 0 reads as a broken
150
+ // viewport (#1902). When the page cannot be read, leave the size out.
151
+ const size = await deps.pool?.viewportOfLease(session.id);
152
+ return { applied: true, ...size, ...media };
101
153
  }
102
154
  return { ...refused(), recommendation: MEDIA_NEEDS_A_LEASE };
103
155
  }
104
156
  const width = clampDim('width', args['width']);
105
157
  const height = clampDim('height', args['height']);
106
- const session = deps.sessions.resolve(asString(args['sessionId']));
158
+ const session = await deps.sessions.resolveServing(asString(args['sessionId']), (s) => canResize(deps, s));
107
159
  // A lease is resized as the lease. The driven provider resizes its own page, whatever
108
160
  // session was named, so taking it first resized the wrong tab and said `applied: true`.
109
161
  const driven = viewportProvider(deps);
@@ -111,10 +163,12 @@ export const VIEWPORT_TOOLS = [
111
163
  if (provider !== undefined) {
112
164
  const applied = await provider.setViewport(session.url, { width, height });
113
165
  // The driven page takes the size; its media stay as the person's browser has them.
114
- if (applied && media === undefined)
166
+ if (applied && media === undefined) {
115
167
  return { applied: true, width, height };
116
- if (applied)
168
+ }
169
+ if (applied) {
117
170
  return { applied: true, width, height, recommendation: MEDIA_NEEDS_A_LEASE };
171
+ }
118
172
  }
119
173
  // A lease is a Playwright-owned page, so the resize was always possible — this tool just had
120
174
  // no route to it. Tried after the driven provider, on the same rule reticle_network_mock
@@ -125,16 +179,18 @@ export const VIEWPORT_TOOLS = [
125
179
  // only mounts under a breakpoint — could not be driven at a desktop viewport at all, and the
126
180
  // recommendation printed alongside the refusal asked the reader to install a second browser.
127
181
  const leased = await deps.pool?.setViewportLease(session.id, { width, height });
128
- if (true !== leased)
182
+ if (true !== leased) {
129
183
  return refused();
184
+ }
130
185
  // What the page reports after the resize, so `applied` is a reading and not a hope.
131
186
  const observed = await deps.pool?.viewportOfLease(session.id);
132
187
  if (observed !== undefined && (observed.width !== width || observed.height !== height)) {
133
188
  return { applied: false, width, height, observed, ok: false, reason: VIEWPORT_NOT_TAKEN };
134
189
  }
135
190
  const seen = observed === undefined ? {} : { observed };
136
- if (media === undefined)
191
+ if (media === undefined) {
137
192
  return { applied: true, width, height, ...seen };
193
+ }
138
194
  if (true === (await deps.pool?.emulateLease(session.id, media))) {
139
195
  return { applied: true, width, height, ...seen, ...media };
140
196
  }
@@ -144,6 +200,9 @@ export const VIEWPORT_TOOLS = [
144
200
  ];
145
201
  /** The resize went through, but the page still reports another size. */
146
202
  const VIEWPORT_NOT_TAKEN = 'the page still reports the size in `observed` after the resize, so the size was not applied';
203
+ /** Touch support rides on the browser context, so an open lease's pointer cannot be changed. */
204
+ const POINTER_IS_FIXED = "a lease's pointer is fixed when its context opens, so it cannot be changed on a live lease";
205
+ const POINTER_NEEDS_A_NEW_LEASE = 'Acquire a new lease with emulate.pointer: reticle_lease { action: "acquire", emulate: { pointer: "coarse" } }, then call this again with its sessionId.';
147
206
  /** prefers-color-scheme and prefers-reduced-motion are set on a leased page only. */
148
207
  const MEDIA_NEEDS_A_LEASE = 'Media features are set on a leased tab: open one with reticle_lease { action: "acquire", emulate }, then call this again with its sessionId.';
149
208
  function refused() {
@@ -164,8 +223,9 @@ function refused() {
164
223
  * snapshot, click and screenshot on that page read as broken (#1118).
165
224
  */
166
225
  function clampDim(key, value) {
167
- if (value === undefined)
226
+ if (value === undefined) {
168
227
  return MIN_DIM;
228
+ }
169
229
  if ('number' !== typeof value || !Number.isFinite(value)) {
170
230
  throw new Error(`${ReticleTool.VIEWPORT}: \`${key}\` must be a number of CSS px, got ${JSON.stringify(value)} — NOT applied`);
171
231
  }
@@ -9,12 +9,12 @@
9
9
  * The browser is injected via a `Launcher` so all lifecycle logic is testable without real Chromium;
10
10
  * the thin Playwright adapter that satisfies these interfaces lives separately.
11
11
  */
12
- import { type SeedStorage } from '@reticlehq/core';
13
- import type { Launcher, PooledMockRule, ScreenshotOptions } from './pool-contract.js';
12
+ import type { Launcher, PooledCoverage, PooledMockRule, ScreenshotOptions } from './pool-contract.js';
14
13
  import type { RealInputArgs, RealInputResult } from '../input/real-input.js';
15
- import { type LeaseEmulation, type LeaseMedia } from './lease-emulation.js';
16
- export type { InitScriptHandle, Launcher, PooledBrowser, PooledContext, PooledCookie, PooledDialog, PooledMockRule, PooledPage, } from './pool-contract.js';
17
- import { type BrowserPoolOptions, type Lease } from './pool-lease.js';
14
+ import { type LeaseMedia } from './lease-emulation.js';
15
+ import { type DeterminismStep } from './determinism.js';
16
+ export type { InitScriptHandle, Launcher, PooledBrowser, PooledContext, PooledCookie, PooledDialog, PooledMockRule, PooledPage, PooledPageError, } from './pool-contract.js';
17
+ import { type AcquireOptions, type BrowserPoolOptions, type Lease } from './pool-lease.js';
18
18
  export { DEFAULT_LEASE_TTL_MS, NO_HEADED_BROWSER, ORPHANED_LEASE_GRACE_MS, PoolAtCapacityError, } from './pool-lease.js';
19
19
  export type { Lease } from './pool-lease.js';
20
20
  /**
@@ -101,6 +101,8 @@ export declare class BrowserPool {
101
101
  * reaper take it out from under a diff.
102
102
  */
103
103
  screenshotLease(sessionId: string, opts?: ScreenshotOptions): Promise<Uint8Array | undefined>;
104
+ /** The reason {@link screenshotLease} last failed for this session, if it did. */
105
+ leaseCaptureError(sessionId: string): string | undefined;
104
106
  /** The URL a lease was opened on — the one that carries its Reticle identity. */
105
107
  leaseUrl(sessionId: string): string | undefined;
106
108
  /**
@@ -117,6 +119,8 @@ export declare class BrowserPool {
117
119
  * Touches the lease like any other tool call, so hovering keeps it alive.
118
120
  */
119
121
  hoverLease(sessionId: string, x: number, y: number): Promise<boolean>;
122
+ /** Reload a lease with the browser cache ignored, or false when this is not a lease or its page cannot. */
123
+ hardReloadLease(sessionId: string): Promise<boolean>;
120
124
  /** Set a live lease's media features, or false when this is not a lease or its page cannot. */
121
125
  emulateLease(sessionId: string, media: LeaseMedia): Promise<boolean>;
122
126
  /** Whether this lease's page is still loading. False for a non-lease, or a page that cannot say. */
@@ -153,6 +157,10 @@ export declare class BrowserPool {
153
157
  height: number;
154
158
  } | undefined>;
155
159
  setMocksLease(sessionId: string, rules: readonly PooledMockRule[]): Promise<boolean>;
160
+ /** Step a deterministic lease's clock by `frames` frames; undefined when it is not one. */
161
+ stepLease(sessionId: string, frames: number): Promise<DeterminismStep | undefined>;
162
+ /** The JS coverage of a lease's page, or undefined when this is not a lease or cannot measure. */
163
+ coverageOf(sessionId: string): PooledCoverage | undefined;
156
164
  /** Guard a leased page's requests, or false when this is not a lease or its page cannot. */
157
165
  guardLease(sessionId: string, decide: (method: string, url: string) => string | undefined): Promise<boolean>;
158
166
  /** What a lease was granted when it opened, or undefined for a session that is not a lease. */
@@ -205,31 +213,7 @@ export declare class BrowserPool {
205
213
  * waits FIFO until a slot frees (or until `signal` aborts, if provided), unless `whenFull` is
206
214
  * `'refuse'`: then it throws {@link PoolAtCapacityError} at once and claims nothing.
207
215
  */
208
- acquire(url: string, opts?: {
209
- signal?: AbortSignal;
210
- /**
211
- * `'wait'` (the default) queues behind the cap. `'refuse'` is for a caller that has a person
212
- * or an agent waiting on the answer and no way to see a queue: it is told the pool is full.
213
- */
214
- whenFull?: 'wait' | 'refuse';
215
- sessionId?: string;
216
- seedStorage?: SeedStorage;
217
- owner?: string;
218
- /** In a browser with a window the person can watch. */
219
- headed?: boolean;
220
- /**
221
- * JavaScript installed before any page script runs, for this lease only. A view built for a
222
- * desktop shell calls bindings the shell injects (`window.go`, `window.runtime`, a preload
223
- * object) on mount; a stub here is what lets it reach its data path in a leased tab.
224
- */
225
- initScript?: string;
226
- /** Granted on the lease's origin before the first navigation, so the first render sees them. */
227
- permissions?: readonly string[];
228
- /** Accept a self-signed or local-CA dev certificate for this lease's context only (#1255). */
229
- ignoreHTTPSErrors?: boolean;
230
- /** Media and viewport set on the context, so the first paint already has them. */
231
- emulate?: LeaseEmulation;
232
- }): Promise<Lease>;
216
+ acquire(url: string, opts?: AcquireOptions): Promise<Lease>;
233
217
  /**
234
218
  * The websocket address this lease's page reported it could not reach, if it reported one.
235
219
  *
@@ -243,6 +227,8 @@ export declare class BrowserPool {
243
227
  lastDialogMessage(sessionId: string): string | undefined;
244
228
  /** Close every context and the browser. Pending waiters are rejected (the pool is terminal now). */
245
229
  shutdown(): Promise<void>;
230
+ /** The origin added to this lease's page CSP `connect-src`, or undefined if none was (#2016). */
231
+ cspWidenedFor(sessionId: string): string | undefined;
246
232
  /** Whether this lease's context skips certificate checks. A context cannot change it later. */
247
233
  ignoresHTTPSErrors(sessionId: string): boolean;
248
234
  /** Whether this lease lives in the headed browser. */
@@ -14,10 +14,12 @@ import { log } from '../../log.js';
14
14
  import { closeLeaseTab, LEASE_CLOSE_TIMED_OUT, settledWithin } from './bounded-close.js';
15
15
  import { seedStorageInto, targetOriginOf } from './storage-seed.js';
16
16
  import { installLeaseMarker, leaseMarkerOf } from './lease-marker-stamp.js';
17
- import { grantLeasePermissions, readNotificationPermission } from './context-permissions.js';
17
+ import { grantLeasePermissions, grantLocalNetworkAccess, readNotificationPermission, widenLeaseCsp, } from './context-permissions.js';
18
18
  import { readViewport } from './lease-emulation.js';
19
- import { NotHtmlDocumentError, READER_NOT_HTML } from './zero-install.js';
20
- import { DEFAULT_LEASE_TTL_MS, DEFAULT_NAV_TIMEOUT_MS, INIT_SCRIPT_UNSUPPORTED, MAX_REMEMBERED_REAPED_LEASES, NO_HEADED_BROWSER, ORPHANED_LEASE_GRACE_MS, pageLoading, PoolAtCapacityError, } from './pool-lease.js';
19
+ import { injectReaderInto } from './zero-install.js';
20
+ import { stepPage } from './determinism.js';
21
+ import { DEFAULT_LEASE_TTL_MS, DEFAULT_NAV_TIMEOUT_MS, INIT_SCRIPT_UNSUPPORTED, MAX_REMEMBERED_REAPED_LEASES, NO_HEADED_BROWSER, ORPHANED_LEASE_GRACE_MS, navStatusOf, pageLoading, PoolAtCapacityError, } from './pool-lease.js';
22
+ import { listenForPageErrors, reportLeaseClosed, } from './lease-page-error.js';
21
23
  export { DEFAULT_LEASE_TTL_MS, NO_HEADED_BROWSER, ORPHANED_LEASE_GRACE_MS, PoolAtCapacityError, } from './pool-lease.js';
22
24
  /**
23
25
  * Owns a headless browser (and, when asked, a headed one) and leases isolated contexts out of them,
@@ -30,6 +32,8 @@ export class BrowserPool {
30
32
  #now;
31
33
  #ttl;
32
34
  #zeroInstall;
35
+ #daemonOrigin;
36
+ #pageErrorHooks;
33
37
  #navTimeout;
34
38
  #headless;
35
39
  #headed;
@@ -73,6 +77,8 @@ export class BrowserPool {
73
77
  this.#ttl = opts.leaseTtlMs ?? DEFAULT_LEASE_TTL_MS;
74
78
  this.#navTimeout = opts.navTimeoutMs ?? DEFAULT_NAV_TIMEOUT_MS;
75
79
  this.#zeroInstall = opts.zeroInstallScript;
80
+ this.#daemonOrigin = opts.daemonOrigin;
81
+ this.#pageErrorHooks = opts;
76
82
  }
77
83
  /** The TTL configured for leases — how long an untouched lease lives before the reaper reclaims it. */
78
84
  leaseTtlMs() {
@@ -109,17 +115,9 @@ export class BrowserPool {
109
115
  return undefined;
110
116
  }
111
117
  for (const [leaseId, lease] of this.#active) {
112
- if (lease.owner !== owner) {
113
- continue;
114
- }
115
- let leaseOrigin;
116
- try {
117
- leaseOrigin = new URL(lease.url).origin;
118
- }
119
- catch {
120
- continue;
121
- }
122
- if (leaseOrigin !== origin) {
118
+ if (lease.owner !== owner ||
119
+ !URL.canParse(lease.url) ||
120
+ new URL(lease.url).origin !== origin) {
123
121
  continue;
124
122
  }
125
123
  for (const [alias, id] of this.#aliases) {
@@ -247,15 +245,21 @@ export class BrowserPool {
247
245
  return undefined;
248
246
  }
249
247
  this.touch(sessionId);
248
+ delete lease.captureError;
250
249
  try {
251
250
  return await lease.page.screenshot(opts);
252
251
  }
253
- catch {
254
- // A capture that fails is not a broken lease. Report "could not capture" and leave the lease
255
- // usable — the alternative is losing a working context to one bad frame.
252
+ catch (error) {
253
+ // A capture that fails is not a broken lease. Keep the reason and leave the lease usable,
254
+ // so the screenshot error says why the browser capture failed.
255
+ lease.captureError = error instanceof Error ? error.message : String(error);
256
256
  return undefined;
257
257
  }
258
258
  }
259
+ /** The reason {@link screenshotLease} last failed for this session, if it did. */
260
+ leaseCaptureError(sessionId) {
261
+ return this.#active.get(this.#leaseIdOf(sessionId))?.captureError;
262
+ }
259
263
  /** The URL a lease was opened on — the one that carries its Reticle identity. */
260
264
  leaseUrl(sessionId) {
261
265
  return this.#active.get(this.#leaseIdOf(sessionId))?.url;
@@ -300,6 +304,21 @@ export class BrowserPool {
300
304
  return false;
301
305
  }
302
306
  }
307
+ /** Reload a lease with the browser cache ignored, or false when this is not a lease or its page cannot. */
308
+ async hardReloadLease(sessionId) {
309
+ const lease = this.#active.get(this.#leaseIdOf(sessionId));
310
+ if (lease?.page.reloadIgnoringCache === undefined) {
311
+ return false;
312
+ }
313
+ this.touch(sessionId);
314
+ try {
315
+ await lease.page.reloadIgnoringCache();
316
+ return true;
317
+ }
318
+ catch {
319
+ return false;
320
+ }
321
+ }
303
322
  /** Set a live lease's media features, or false when this is not a lease or its page cannot. */
304
323
  async emulateLease(sessionId, media) {
305
324
  const lease = this.#active.get(this.#leaseIdOf(sessionId));
@@ -392,6 +411,17 @@ export class BrowserPool {
392
411
  return false;
393
412
  }
394
413
  }
414
+ /** Step a deterministic lease's clock by `frames` frames; undefined when it is not one. */
415
+ stepLease(sessionId, frames) {
416
+ const page = this.#active.get(this.#leaseIdOf(sessionId))?.page;
417
+ this.touch(sessionId);
418
+ return page === undefined ? Promise.resolve(undefined) : stepPage(page, frames);
419
+ }
420
+ /** The JS coverage of a lease's page, or undefined when this is not a lease or cannot measure. */
421
+ coverageOf(sessionId) {
422
+ this.touch(sessionId);
423
+ return this.#active.get(this.#leaseIdOf(sessionId))?.page.jsCoverage;
424
+ }
395
425
  /** Guard a leased page's requests, or false when this is not a lease or its page cannot. */
396
426
  async guardLease(sessionId, decide) {
397
427
  const lease = this.#active.get(this.#leaseIdOf(sessionId));
@@ -510,16 +540,17 @@ export class BrowserPool {
510
540
  const browser = await this.#ensureBrowser(slot);
511
541
  const contextOpts = {
512
542
  ...(true === opts.ignoreHTTPSErrors ? { ignoreHTTPSErrors: true } : {}),
543
+ ...(true === opts.jsCoverage ? { jsCoverage: true } : {}),
513
544
  ...opts.emulate,
514
545
  };
515
546
  context = await browser.newContext(0 < Object.keys(contextOpts).length ? contextOpts : undefined);
516
547
  if (opts.permissions !== undefined) {
517
548
  await grantLeasePermissions(context, opts.permissions, url);
518
549
  }
550
+ await grantLocalNetworkAccess(context, url);
551
+ const csp = await widenLeaseCsp(context, url, this.#daemonOrigin);
519
552
  const page = await context.newPage();
520
- // A caller-supplied init script is a stub of the bindings a desktop shell injects, and it has
521
- // to exist before the app's first module runs. Installed here — before goto — so the very
522
- // first document gets it, and kept for the lease (see hostStub) so a reload keeps it too.
553
+ // A desktop-shell stub must exist before the app's first module: added before goto, and kept.
523
554
  let hostStub;
524
555
  if (opts.initScript !== undefined) {
525
556
  const added = await page.addInitScript?.(opts.initScript);
@@ -549,9 +580,8 @@ export class BrowserPool {
549
580
  pending = dialled;
550
581
  } // logged during goto, before the lease is registered below
551
582
  });
552
- // Same "listen before goto" reasoning as onConsole above: a dialog opened during the initial
553
- // navigation (an app that confirms something in an onload handler) must still be dismissed, not
554
- // just ones opened after the lease is registered.
583
+ // Listen before goto, as onConsole does: a dialog in an onload handler is dismissed too. The SDK
584
+ // answers a dialog from its own input first; the hook records this "no" for the verdict (#1350).
555
585
  page.onDialog?.((dialog) => {
556
586
  const active = this.#active.get(sessionId);
557
587
  if (active !== undefined) {
@@ -561,7 +591,10 @@ export class BrowserPool {
561
591
  pendingDialogMessage = dialog.message;
562
592
  }
563
593
  void dialog.dismiss();
594
+ this.#pageErrorHooks?.onDialog?.(sessionId, dialog);
564
595
  });
596
+ // Listener on BEFORE goto — an import-time throw lands during it (#1966).
597
+ await listenForPageErrors(page, sessionId, this.#pageErrorHooks);
565
598
  const marker = leaseMarkerOf(url);
566
599
  if (marker?.session === sessionId) {
567
600
  await installLeaseMarker(page, sessionId, targetOriginOf(url), marker.project);
@@ -593,24 +626,7 @@ export class BrowserPool {
593
626
  // Foreground it: a page opened beside other tabs can start hidden (#1351). Best-effort — a tab
594
627
  // that stays hidden is reported by name at the lease, which failing here would lose.
595
628
  await page.bringToFront?.().catch(() => undefined);
596
- let navStatus;
597
- if (null !== navRes && 'object' === typeof navRes && 'status' in navRes) {
598
- const s = navRes.status;
599
- if ('function' === typeof s) {
600
- try {
601
- const code = s.call(navRes);
602
- if ('number' === typeof code) {
603
- navStatus = code;
604
- }
605
- }
606
- catch {
607
- // ignore
608
- }
609
- }
610
- else if ('number' === typeof s) {
611
- navStatus = s;
612
- }
613
- }
629
+ const navStatus = navStatusOf(navRes);
614
630
  this.#active.set(sessionId, {
615
631
  headed: slot.headed,
616
632
  context,
@@ -620,6 +636,7 @@ export class BrowserPool {
620
636
  touchedAt: this.#now(),
621
637
  permissions: opts.permissions ?? [],
622
638
  ignoresHTTPSErrors: true === opts.ignoreHTTPSErrors,
639
+ csp,
623
640
  ...(pending === undefined ? {} : { dialFailureUrl: pending }),
624
641
  ...(pendingDialogMessage === undefined ? {} : { lastDialogMessage: pendingDialogMessage }),
625
642
  ...(hostStub === undefined || opts.initScript === undefined
@@ -631,17 +648,7 @@ export class BrowserPool {
631
648
  url,
632
649
  ...(navStatus !== undefined ? { navStatus } : {}),
633
650
  release: () => this.#release(sessionId),
634
- injectReader: async () => {
635
- const script = this.#zeroInstall?.();
636
- if (script === undefined || page.evaluate === undefined) {
637
- return false;
638
- }
639
- await page.addInitScript?.(script);
640
- if (READER_NOT_HTML === (await page.evaluate(script))) {
641
- throw new NotHtmlDocumentError();
642
- }
643
- return true;
644
- },
651
+ injectReader: () => injectReaderInto(page, this.#zeroInstall?.(url)),
645
652
  isLoading: () => pageLoading(page),
646
653
  };
647
654
  }
@@ -703,6 +710,7 @@ export class BrowserPool {
703
710
  return;
704
711
  } // already released or lost to a crash
705
712
  this.#active.delete(sessionId);
713
+ reportLeaseClosed(this.#pageErrorHooks, sessionId);
706
714
  // Drop any alias pointing here, or the map grows for the life of the daemon and a later lease
707
715
  // reusing that registered name would resolve to a context that is already closed.
708
716
  for (const [registered, leaseId] of this.#aliases) {
@@ -863,6 +871,10 @@ export class BrowserPool {
863
871
  waiter();
864
872
  } // let them re-claim + relaunch
865
873
  }
874
+ /** The origin added to this lease's page CSP `connect-src`, or undefined if none was (#2016). */
875
+ cspWidenedFor(sessionId) {
876
+ return this.#active.get(this.#leaseIdOf(sessionId))?.csp.widenedFor;
877
+ }
866
878
  /** Whether this lease's context skips certificate checks. A context cannot change it later. */
867
879
  ignoresHTTPSErrors(sessionId) {
868
880
  return true === this.#active.get(this.#leaseIdOf(sessionId))?.ignoresHTTPSErrors;
@@ -1,20 +1,3 @@
1
- /**
2
- * Browser permissions on a leased context: granted before the first navigation, and read back where
3
- * the browser can contradict the grant.
4
- *
5
- * Never changed on a lease that is already open. Acquire hands an origin's live lease to whoever asks
6
- * for that origin, so the lease may be another agent's tab mid-flow, and nothing here knows whose it
7
- * is. A different set means a fresh lease.
8
- *
9
- * Each grant is scoped to the origin the lease was opened on, so a third-party frame the app embeds
10
- * is not handed the same grant as the app.
11
- *
12
- * Notifications are why the read-back exists. Playwright's headless shell — what the pool launches
13
- * when Playwright's own Chromium is installed — has no notification support: after a grant,
14
- * `navigator.permissions.query` reports `granted` while `Notification.permission` still reads
15
- * `denied`. A page that gates its UI on the second never sees the grant, so a lease that reported
16
- * "granted" there would be reporting something the page cannot see.
17
- */
18
1
  import type { PooledContext, PooledPage } from './pool-contract.js';
19
2
  /** The permission whose grant the headless shell does not pass on to `Notification.permission`. */
20
3
  export declare const NOTIFICATIONS_PERMISSION = "notifications";
@@ -31,6 +14,19 @@ export declare class LeasePermissionError extends Error {
31
14
  }
32
15
  /** Grant `permissions` on the origin of `url`. Nothing to grant is a no-op, not a refusal. */
33
16
  export declare function grantLeasePermissions(context: PooledContext, permissions: readonly string[], url: string): Promise<void>;
17
+ /** The origin of `url` when the page is not on loopback, else undefined (also for no url). */
18
+ export declare function offLoopbackOrigin(url: string | undefined): string | undefined;
19
+ /** Playwright's name for Chrome's Local Network Access grant (local + loopback network). */
20
+ export declare const LOCAL_NETWORK_ACCESS = "local-network-access";
21
+ /**
22
+ * Let a page that is not on loopback dial the bridge on loopback. Chromium blocks that unless a
23
+ * person grants Local Network Access, and a leased browser has no person, so a lease on a hosted
24
+ * preview loaded, signed in, and answered `sdk_never_dialled`. A loopback page needs no grant.
25
+ *
26
+ * ponytail: best effort. A Playwright older than the permission name refuses it; the lease then
27
+ * opens as before, and a page that cannot dial still reports `sdk_never_dialled`.
28
+ */
29
+ export declare function grantLocalNetworkAccess(context: PooledContext, url: string): Promise<void>;
34
30
  /**
35
31
  * What the page's `Notification.permission` says, or undefined when it cannot be asked.
36
32
  *
@@ -39,3 +35,24 @@ export declare function grantLeasePermissions(context: PooledContext, permission
39
35
  * instead of holding the caller.
40
36
  */
41
37
  export declare function readNotificationPermission(page: PooledPage): Promise<string | undefined>;
38
+ /** How long the loopback CSP probe may take. Bounded because acquire waits on it. */
39
+ export declare const CSP_PROBE_TIMEOUT_MS = 2000;
40
+ /** The part of `fetch` the probe uses. */
41
+ export type CspProbeFetch = (url: string, init: {
42
+ signal: AbortSignal;
43
+ }) => Promise<Pick<Response, 'headers' | 'text'>>;
44
+ /**
45
+ * Whether the document at `url` sends a CSP (header or meta tag) that would block a dial to
46
+ * `daemonOrigin` (#2163). False when it cannot be asked: the lease then opens as before.
47
+ */
48
+ export declare function pageCspBlocksDaemon(url: string, daemonOrigin: string, probe?: CspProbeFetch): Promise<boolean>;
49
+ /**
50
+ * On a lease whose page's CSP would block the daemon, add `daemonOrigin` to its `connect-src`
51
+ * (#2016). Off loopback this always installs the route. On loopback it installs it only when a
52
+ * probe of the page finds a CSP that blocks the dial (#2163), because the route sees every request
53
+ * in the context, and a dev server that sends no CSP should not pay for it. The returned record
54
+ * gets `widenedFor` once a response was really widened, so the lease can say so.
55
+ */
56
+ export declare function widenLeaseCsp(context: PooledContext, url: string, daemonOrigin: string | undefined, probe?: CspProbeFetch): Promise<{
57
+ widenedFor?: string;
58
+ }>;