@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
@@ -11,6 +11,24 @@ export const ColorScheme = {
11
11
  NO_PREFERENCE: 'no-preference',
12
12
  };
13
13
  export const ReducedMotion = { REDUCE: 'reduce', NO_PREFERENCE: 'no-preference' };
14
+ export const Pointer = { FINE: 'fine', COARSE: 'coarse' };
15
+ /**
16
+ * Translate a lease's emulation into context options. `pointer` is not one of Playwright's: a
17
+ * coarse primary pointer is what touch support produces, so it travels as `hasTouch`.
18
+ *
19
+ * Measured in real headless Chromium: `hasTouch: true` makes `(pointer: coarse)` match and
20
+ * `(pointer: fine)` stop matching, with `maxTouchPoints` at 1. `isMobile` does the same and also
21
+ * changes viewport-meta handling, so it is deliberately left out.
22
+ */
23
+ export function contextEmulationOf(emulate) {
24
+ const { pointer, deterministic, ...rest } = emulate;
25
+ return {
26
+ ...rest,
27
+ ...(pointer === undefined ? {} : { hasTouch: Pointer.COARSE === pointer }),
28
+ // A deterministic page draws at one density on every machine.
29
+ ...(deterministic === undefined ? {} : { deviceScaleFactor: 1 }),
30
+ };
31
+ }
14
32
  /** The page's own layout viewport, read back after a resize. */
15
33
  const VIEWPORT_READ_SCRIPT = '[window.innerWidth, window.innerHeight]';
16
34
  /** The size a page reports for itself, or undefined when it cannot be read. */
@@ -5,6 +5,7 @@ interface LeaseMarkerArg {
5
5
  targetOrigin: string;
6
6
  sessionParam: string;
7
7
  projectParam: string;
8
+ sessionStorageKey: string;
8
9
  }
9
10
  /**
10
11
  * Init script restoring the marker a server redirect dropped, so the SDK registers under the lease id.
@@ -13,6 +14,11 @@ interface LeaseMarkerArg {
13
14
  * target origin (never an identity provider's or any third party's URL), and the pool installs it
14
15
  * only when the navigated URL's marker matches this lease's id. Must stay self-contained: Playwright
15
16
  * serialises the function source into the page.
17
+ *
18
+ * It also writes the lease id to the tab session store before page scripts run. An SPA that calls
19
+ * `history.replaceState` on load drops the URL marker before `connect()`, and the SDK then reads
20
+ * the id from that store (#2026). A value the tab holds already stays: a reload holds the lease id,
21
+ * and a different value is one the SDK stored for an explicit `connect({ session })`.
16
22
  */
17
23
  export declare function stampLeaseMarker(arg: LeaseMarkerArg): void;
18
24
  export declare function installLeaseMarker(page: PooledPage, leaseId: string, targetOrigin: string | undefined, project: string | undefined): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { RETICLE_URL_PARAM } from '@reticlehq/core';
1
+ import { RETICLE_SESSION_STORAGE_KEY, RETICLE_URL_PARAM } from '@reticlehq/core';
2
2
  /**
3
3
  * Init script restoring the marker a server redirect dropped, so the SDK registers under the lease id.
4
4
  * It runs only in pages of the lease's own browser context, so a person's tab at the same URL never
@@ -6,11 +6,24 @@ import { RETICLE_URL_PARAM } from '@reticlehq/core';
6
6
  * target origin (never an identity provider's or any third party's URL), and the pool installs it
7
7
  * only when the navigated URL's marker matches this lease's id. Must stay self-contained: Playwright
8
8
  * serialises the function source into the page.
9
+ *
10
+ * It also writes the lease id to the tab session store before page scripts run. An SPA that calls
11
+ * `history.replaceState` on load drops the URL marker before `connect()`, and the SDK then reads
12
+ * the id from that store (#2026). A value the tab holds already stays: a reload holds the lease id,
13
+ * and a different value is one the SDK stored for an explicit `connect({ session })`.
9
14
  */
10
15
  export function stampLeaseMarker(arg) {
11
16
  const win = globalThis.window;
12
17
  if (win === undefined || win.top !== win || win.location.origin !== arg.targetOrigin)
13
18
  return;
19
+ try {
20
+ if (null === win.sessionStorage.getItem(arg.sessionStorageKey)) {
21
+ win.sessionStorage.setItem(arg.sessionStorageKey, arg.session);
22
+ }
23
+ }
24
+ catch {
25
+ // A page with storage blocked still gets the URL marker below.
26
+ }
14
27
  try {
15
28
  const url = new URL(win.location.href);
16
29
  if (url.searchParams.has(arg.sessionParam))
@@ -34,6 +47,7 @@ export async function installLeaseMarker(page, leaseId, targetOrigin, project) {
34
47
  targetOrigin,
35
48
  sessionParam: RETICLE_URL_PARAM.SESSION,
36
49
  projectParam: RETICLE_URL_PARAM.PROJECT,
50
+ sessionStorageKey: RETICLE_SESSION_STORAGE_KEY,
37
51
  });
38
52
  }
39
53
  export function leaseMarkerOf(url) {
@@ -0,0 +1,56 @@
1
+ import type { PooledDialog, PooledPage, PooledPageError } from './pool-contract.js';
2
+ /** The page-error hooks `BrowserPoolOptions` carries, forwarded here verbatim. */
3
+ export interface LeasePageErrorHooks {
4
+ onPageError?: ((leaseId: string, error: PooledPageError) => void) | undefined;
5
+ onLeaseClosed?: ((leaseId: string) => void) | undefined;
6
+ /** Each native dialog the pool dismissed. See `BrowserPoolOptions.onDialog`. */
7
+ onDialog?: ((leaseId: string, dialog: Pick<PooledDialog, 'kind' | 'message'>) => void) | undefined;
8
+ }
9
+ /**
10
+ * Install the lease's uncaught-error listener and its early console queue. Call BEFORE the first
11
+ * navigation — the timing is the whole fix: an import-time throw lands during `goto`, and one
12
+ * attached after it reads a broken page as healthy. Forwarded with the lease id, because the
13
+ * session for this lease usually does not exist yet, so the sink buffers.
14
+ */
15
+ export declare function listenForPageErrors(page: PooledPage, leaseId: string, hooks: LeasePageErrorHooks | undefined): Promise<void>;
16
+ /**
17
+ * Report a closed lease — manual release, reaper and crash reclaim all funnel through the pool's
18
+ * one release path. The wiring owns whether anything was kept FOR that lease and drops it.
19
+ */
20
+ export declare function reportLeaseClosed(hooks: LeasePageErrorHooks | undefined, leaseId: string): void;
21
+ /** How many console calls and error events a page keeps for an SDK that has not installed yet. */
22
+ export declare const EARLY_CAPTURE_LIMIT = 200;
23
+ /**
24
+ * The argument for `queueEarlyConsole`. Every driven page gets the same one: a pool lease, and
25
+ * `verify`'s own page. It looks for the SDK every `pollMs`, for at most `maxPolls` looks (30 s).
26
+ */
27
+ export declare const EARLY_CAPTURE_ARG: {
28
+ key: string;
29
+ limit: number;
30
+ pollMs: number;
31
+ maxPolls: number;
32
+ };
33
+ interface EarlyCaptureArg {
34
+ key: string;
35
+ limit: number;
36
+ pollMs: number;
37
+ maxPolls: number;
38
+ }
39
+ /**
40
+ * Init script: keep the console errors and warnings a page logs before the SDK installs.
41
+ *
42
+ * An app connects the SDK from a module script or a bundle, and both run after the page's own
43
+ * classic scripts. An error such a script logs while the page loads was lost, so "no console
44
+ * errors" passed on a page that logged one on every load. Playwright's own console event is not a
45
+ * substitute: it also reports messages the browser writes itself (a failed WebSocket, which is how
46
+ * the SDK's own first dial can fail), and the SDK's console channel has never counted those.
47
+ *
48
+ * So this keeps the RAW calls and events. Once the SDK has wrapped the console, this hands them to
49
+ * it: each call goes through the console again and each event is dispatched again, so the SDK's own
50
+ * handlers report them and the meaning of "console error" stays the SDK's. The SDK needs no code for
51
+ * this, so a page that Reticle does not drive downloads nothing for it. Entries the bound did not
52
+ * keep are declared with one console warning. Must stay self-contained: Playwright serialises the
53
+ * function source into the page.
54
+ */
55
+ export declare function queueEarlyConsole(arg: EarlyCaptureArg): void;
56
+ export {};
@@ -0,0 +1,160 @@
1
+ /**
2
+ * The page-error side of a lease: listen for the throws the SDK will never see, and tell the
3
+ * wiring when the lease is gone.
4
+ *
5
+ * An app whose module graph throws during the first navigation fires its `error` event while the
6
+ * page still has no Reticle SDK in it — the zero-install reader is only evaluated after `goto`
7
+ * resolves — so nothing on the page records it, and a blank page reported as a bare console-error
8
+ * count (#1966). The server's Playwright handle is attached before the navigation, so it is the
9
+ * one witness that cannot miss the throw. This module owns that wiring for `BrowserPool`; the
10
+ * journal that buffers and journals what arrives lives in `page-error-journal.ts`.
11
+ */
12
+ import { RETICLE_EARLY_CAPTURE } from '@reticlehq/core';
13
+ /**
14
+ * Install the lease's uncaught-error listener and its early console queue. Call BEFORE the first
15
+ * navigation — the timing is the whole fix: an import-time throw lands during `goto`, and one
16
+ * attached after it reads a broken page as healthy. Forwarded with the lease id, because the
17
+ * session for this lease usually does not exist yet, so the sink buffers.
18
+ */
19
+ export async function listenForPageErrors(page, leaseId, hooks) {
20
+ page.onPageError?.((error) => {
21
+ hooks?.onPageError?.(leaseId, error);
22
+ });
23
+ await page.addInitScript?.(queueEarlyConsole, EARLY_CAPTURE_ARG);
24
+ }
25
+ /**
26
+ * Report a closed lease — manual release, reaper and crash reclaim all funnel through the pool's
27
+ * one release path. The wiring owns whether anything was kept FOR that lease and drops it.
28
+ */
29
+ export function reportLeaseClosed(hooks, leaseId) {
30
+ hooks?.onLeaseClosed?.(leaseId);
31
+ }
32
+ /** How many console calls and error events a page keeps for an SDK that has not installed yet. */
33
+ export const EARLY_CAPTURE_LIMIT = 200;
34
+ /**
35
+ * The argument for `queueEarlyConsole`. Every driven page gets the same one: a pool lease, and
36
+ * `verify`'s own page. It looks for the SDK every `pollMs`, for at most `maxPolls` looks (30 s).
37
+ */
38
+ export const EARLY_CAPTURE_ARG = {
39
+ key: RETICLE_EARLY_CAPTURE,
40
+ limit: EARLY_CAPTURE_LIMIT,
41
+ pollMs: 50,
42
+ maxPolls: 600,
43
+ };
44
+ /**
45
+ * Init script: keep the console errors and warnings a page logs before the SDK installs.
46
+ *
47
+ * An app connects the SDK from a module script or a bundle, and both run after the page's own
48
+ * classic scripts. An error such a script logs while the page loads was lost, so "no console
49
+ * errors" passed on a page that logged one on every load. Playwright's own console event is not a
50
+ * substitute: it also reports messages the browser writes itself (a failed WebSocket, which is how
51
+ * the SDK's own first dial can fail), and the SDK's console channel has never counted those.
52
+ *
53
+ * So this keeps the RAW calls and events. Once the SDK has wrapped the console, this hands them to
54
+ * it: each call goes through the console again and each event is dispatched again, so the SDK's own
55
+ * handlers report them and the meaning of "console error" stays the SDK's. The SDK needs no code for
56
+ * this, so a page that Reticle does not drive downloads nothing for it. Entries the bound did not
57
+ * keep are declared with one console warning. Must stay self-contained: Playwright serialises the
58
+ * function source into the page.
59
+ */
60
+ export function queueEarlyConsole(arg) {
61
+ const win = globalThis.window;
62
+ if (win === undefined || win[arg.key] !== undefined) {
63
+ return;
64
+ }
65
+ const capture = { queue: [], dropped: 0 };
66
+ win[arg.key] = capture;
67
+ const keep = (item) => {
68
+ if (win[arg.key] !== capture) {
69
+ return;
70
+ }
71
+ if (capture.queue.length < arg.limit) {
72
+ capture.queue.push(item);
73
+ }
74
+ else {
75
+ capture.dropped += 1;
76
+ }
77
+ };
78
+ const mine = {};
79
+ for (const method of ['error', 'warn']) {
80
+ const original = win.console[method];
81
+ const wrapper = (...args) => {
82
+ keep({ method, args });
83
+ original.apply(win.console, args);
84
+ };
85
+ mine[method] = wrapper;
86
+ win.console[method] = wrapper;
87
+ }
88
+ win.addEventListener('error', (event) => keep({ event }), true);
89
+ win.addEventListener('unhandledrejection', (event) => keep({ event }), true);
90
+ // A copy of a kept event: an event that has been dispatched once is copied, not sent again.
91
+ const copy = (event) => {
92
+ if ('reason' in event) {
93
+ return new win.PromiseRejectionEvent('unhandledrejection', {
94
+ promise: event['promise'],
95
+ reason: event['reason'],
96
+ });
97
+ }
98
+ const init = {};
99
+ for (const field of ['message', 'filename', 'lineno', 'colno', 'error']) {
100
+ if (event[field] !== undefined) {
101
+ init[field] = event[field];
102
+ }
103
+ }
104
+ return new win.ErrorEvent('error', init);
105
+ };
106
+ // A `<script>`, `<img>` or `<link>` that failed to load dispatches a plain `Event` on its element,
107
+ // and only the element names the URL. A copy on `window` loses it, and the SDK reported an error
108
+ // with no message, no source and line 0 (#2220). So such an event is sent again from the same
109
+ // element, and the SDK's capture listener on `window` reads the tag and the URL. A listener added
110
+ // after the SDK's stops it on `window`, so the page's own handlers on the element do not run twice.
111
+ // An element that left the page cannot reach `window`, so it gets the copy.
112
+ const resend = (event) => {
113
+ const target = event['target'];
114
+ if ('message' in event || 'reason' in event || true !== target?.isConnected) {
115
+ return false;
116
+ }
117
+ const again = new win.Event('error');
118
+ const stop = (seen) => {
119
+ if (seen === again) {
120
+ again.stopPropagation();
121
+ }
122
+ };
123
+ win.addEventListener('error', stop, true);
124
+ try {
125
+ target.dispatchEvent?.(again);
126
+ }
127
+ finally {
128
+ win.removeEventListener('error', stop, true);
129
+ }
130
+ return true;
131
+ };
132
+ let polls = 0;
133
+ const timer = win.setInterval(() => {
134
+ polls += 1;
135
+ // The SDK has installed when the console is no longer this script's own wrapper.
136
+ const installed = win.console.error !== mine.error;
137
+ if (!installed && polls < arg.maxPolls) {
138
+ return;
139
+ }
140
+ win.clearInterval(timer);
141
+ win[arg.key] = null;
142
+ if (!installed) {
143
+ return;
144
+ }
145
+ for (const item of capture.queue) {
146
+ if ('method' in item) {
147
+ win.console[item['method']](...item['args']);
148
+ }
149
+ else {
150
+ const event = item['event'];
151
+ if (!resend(event)) {
152
+ win.dispatchEvent(copy(event));
153
+ }
154
+ }
155
+ }
156
+ if (capture.dropped > 0) {
157
+ win.console.warn(`reticle: ${String(capture.dropped)} early console entries were not kept`);
158
+ }
159
+ }, arg.pollMs);
160
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Journal the uncaught page errors the SDK was never installed in time to see.
3
+ *
4
+ * An app whose module graph throws during the first navigation fires its `error` event while the
5
+ * page still has no Reticle SDK in it — the zero-install reader is only evaluated after `goto`
6
+ * resolves. The agent then saw a blank page and a bare console-error count, and had to re-run the
7
+ * app under `--enable-logging` to learn why (#1966). The pool's Playwright handle is attached
8
+ * before the navigation, so the server is the one witness that cannot miss the throw.
9
+ *
10
+ * The pool hands errors here with the lease id. The session for that lease usually does not exist
11
+ * yet, so errors buffer per lease and flush the moment the session connects
12
+ * (`bridge.attachSessionCreate`). One already connected means the error is journaled at once.
13
+ *
14
+ * The daemon runs for days, so both maps are told to forget: a lease that is released or reaped
15
+ * has its buffer dropped (`onLeaseClosed`), and a session that has ended has its dedupe memory
16
+ * dropped (`onSessionEnd`). Without those, every failed lease and every ended session leaves an
17
+ * entry behind forever.
18
+ *
19
+ * Deduplication: when the SDK IS installed it reports the same throw as `error.uncaught` — with
20
+ * the browser's `Uncaught ` prefix on the message, which Playwright's spelling lacks. Both
21
+ * spellings fold into one identity (`uncaughtIdentity`), and whichever source is seen first
22
+ * suppresses the other's copy. A journal write is deferred by a short window first, so an SDK
23
+ * event racing the page error lands inside the window and wins. The reverse race — our copy
24
+ * first, the SDK's right after — can still journal both; the SDK's own event is authoritative and
25
+ * only the redundant second copy is cost, not wrong evidence.
26
+ */
27
+ import { type ReticleEvent } from '@reticlehq/core';
28
+ import type { PooledDialog, PooledPageError } from './pool-contract.js';
29
+ /** The session surface the journal needs. The real `Session` satisfies this structurally. */
30
+ export interface JournalableSession {
31
+ readonly id: string;
32
+ readonly url?: string;
33
+ pushEvent(event: ReticleEvent): void;
34
+ eventsSince(cursor: number): ReticleEvent[];
35
+ onEvent(listener: (event: ReticleEvent) => void): () => void;
36
+ }
37
+ export interface PageErrorJournal {
38
+ /** The pool's sink — hand it to `BrowserPoolOptions.onPageError`. */
39
+ onPoolPageError(leaseId: string, error: PooledPageError): void;
40
+ /** The bridge hook — register with `bridge.attachSessionCreate`. */
41
+ onSessionCreate(session: JournalableSession): void;
42
+ /** The pool's release hook — register with `BrowserPoolOptions.onLeaseClosed`. */
43
+ onLeaseClosed(leaseId: string): void;
44
+ /** The bridge hook — register with `bridge.attachSessionEnd`. */
45
+ onSessionEnd(sessionId: string): void;
46
+ /**
47
+ * The pool's dialog sink — hand it to `BrowserPoolOptions.onDialog`. Journals the dismissal as
48
+ * `dialog.opened` on the lease's session, so a verdict reads it as the SDK's own answer. A dialog
49
+ * before the session connects is dropped: no action window can hold it.
50
+ */
51
+ onPoolDialog(leaseId: string, dialog: Pick<PooledDialog, 'kind' | 'message'>): void;
52
+ }
53
+ /**
54
+ * Fold the two spellings of one throw into one identity. Chromium prefixes a window-reported
55
+ * uncaught error with `Uncaught ` and often the error class (`Uncaught TypeError: ...`); the
56
+ * Playwright `pageerror` message is the bare exception text. Everything after the first line is a
57
+ * stack fragment and never matches, so it is dropped.
58
+ */
59
+ export declare function uncaughtIdentity(rawMessage: string): string;
60
+ export declare function makePageErrorJournal(deps: {
61
+ resolveSession: (leaseId: string) => JournalableSession | undefined;
62
+ sleep?: (ms: number) => Promise<void>;
63
+ }): PageErrorJournal;
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Journal the uncaught page errors the SDK was never installed in time to see.
3
+ *
4
+ * An app whose module graph throws during the first navigation fires its `error` event while the
5
+ * page still has no Reticle SDK in it — the zero-install reader is only evaluated after `goto`
6
+ * resolves. The agent then saw a blank page and a bare console-error count, and had to re-run the
7
+ * app under `--enable-logging` to learn why (#1966). The pool's Playwright handle is attached
8
+ * before the navigation, so the server is the one witness that cannot miss the throw.
9
+ *
10
+ * The pool hands errors here with the lease id. The session for that lease usually does not exist
11
+ * yet, so errors buffer per lease and flush the moment the session connects
12
+ * (`bridge.attachSessionCreate`). One already connected means the error is journaled at once.
13
+ *
14
+ * The daemon runs for days, so both maps are told to forget: a lease that is released or reaped
15
+ * has its buffer dropped (`onLeaseClosed`), and a session that has ended has its dedupe memory
16
+ * dropped (`onSessionEnd`). Without those, every failed lease and every ended session leaves an
17
+ * entry behind forever.
18
+ *
19
+ * Deduplication: when the SDK IS installed it reports the same throw as `error.uncaught` — with
20
+ * the browser's `Uncaught ` prefix on the message, which Playwright's spelling lacks. Both
21
+ * spellings fold into one identity (`uncaughtIdentity`), and whichever source is seen first
22
+ * suppresses the other's copy. A journal write is deferred by a short window first, so an SDK
23
+ * event racing the page error lands inside the window and wins. The reverse race — our copy
24
+ * first, the SDK's right after — can still journal both; the SDK's own event is authoritative and
25
+ * only the redundant second copy is cost, not wrong evidence.
26
+ */
27
+ import { CONSOLE_EVENT, EventType, RETICLE_URL_PARAM } from '@reticlehq/core';
28
+ /**
29
+ * What a dismissal answers, per dialog kind the wire knows. The same replies the SDK gives
30
+ * (`observers/dialogs.ts`): `confirm` is "no", `prompt` is no value, `alert` has no answer.
31
+ * A kind not in this map (`beforeunload`) is not a `dialog.opened` and is not reported.
32
+ */
33
+ const DISMISSED_ANSWER = {
34
+ alert: {},
35
+ confirm: { answered: false },
36
+ prompt: { answered: null },
37
+ };
38
+ /** How many errors wait per lease for a session that may never connect. Older ones give way. */
39
+ const MAX_BUFFERED_PER_LEASE = 8;
40
+ /** How many recent error identities a session tracks — the dedupe memory for both sources. */
41
+ const MAX_TRACKED_IDENTITIES = 32;
42
+ /**
43
+ * How long a journal write waits before committing, so an SDK event for the same throw — the
44
+ * websocket hop against a CDP callback — can arrive first and claim the identity.
45
+ */
46
+ const DEDUPE_WAIT_MS = 50;
47
+ /**
48
+ * Fold the two spellings of one throw into one identity. Chromium prefixes a window-reported
49
+ * uncaught error with `Uncaught ` and often the error class (`Uncaught TypeError: ...`); the
50
+ * Playwright `pageerror` message is the bare exception text. Everything after the first line is a
51
+ * stack fragment and never matches, so it is dropped.
52
+ */
53
+ export function uncaughtIdentity(rawMessage) {
54
+ const withoutPrefix = rawMessage.replace(/^Uncaught\s+/i, '');
55
+ const firstLine = withoutPrefix.split('\n')[0] ?? withoutPrefix;
56
+ // Lazy `*?`: the greedy spelling cannot match a BARE `Error: ` — the leading character class
57
+ // eats the `E` and backtracking never gives it back.
58
+ return firstLine.replace(/^[A-Za-z0-9_]*?(?:Error|Exception):\s*/, '');
59
+ }
60
+ /** The first stack frame's file and line — the `source (file:line)` the issue asks the agent to see. */
61
+ const STACK_FRAME = /\bat\s+(\S+?):(\d+):\d+\b/;
62
+ function originOf(stack) {
63
+ if (stack === undefined) {
64
+ return {};
65
+ }
66
+ const frame = STACK_FRAME.exec(stack);
67
+ const source = frame?.[1];
68
+ const line = frame?.[2];
69
+ if (source === undefined || line === undefined) {
70
+ return {};
71
+ }
72
+ return { source, line: Number(line) };
73
+ }
74
+ function leaseParamOf(url) {
75
+ if (url === undefined) {
76
+ return undefined;
77
+ }
78
+ try {
79
+ return new URL(url).searchParams.get(RETICLE_URL_PARAM.SESSION) ?? undefined;
80
+ }
81
+ catch {
82
+ return undefined;
83
+ }
84
+ }
85
+ const realSleep = (ms) => {
86
+ return new Promise((resolve) => {
87
+ setTimeout(resolve, ms);
88
+ });
89
+ };
90
+ export function makePageErrorJournal(deps) {
91
+ const sleep = deps.sleep ?? realSleep;
92
+ /** Errors heard before their session connected, keyed by lease id. */
93
+ const buffered = new Map();
94
+ /** Per session: identities already claimed by the SDK or by us, oldest first. */
95
+ const seen = new Map();
96
+ const mark = (sessionId, identity) => {
97
+ const identities = seen.get(sessionId) ?? [];
98
+ if (identities.includes(identity)) {
99
+ return;
100
+ }
101
+ identities.push(identity);
102
+ if (identities.length > MAX_TRACKED_IDENTITIES) {
103
+ identities.shift();
104
+ }
105
+ seen.set(sessionId, identities);
106
+ };
107
+ const seenBefore = (sessionId, identity) => {
108
+ return (seen.get(sessionId) ?? []).includes(identity);
109
+ };
110
+ const journal = async (session, error, droppedCount) => {
111
+ await sleep(DEDUPE_WAIT_MS);
112
+ const identity = uncaughtIdentity(error.message);
113
+ if (seenBefore(session.id, identity)) {
114
+ return;
115
+ }
116
+ mark(session.id, identity);
117
+ const { source, line } = originOf(error.stack);
118
+ session.pushEvent({
119
+ t: 0, // pushEvent re-stamps with the session's own clock
120
+ type: EventType.CONSOLE_ERROR,
121
+ sessionId: session.id,
122
+ data: {
123
+ message: error.message,
124
+ kind: CONSOLE_EVENT.PAGE_ERROR,
125
+ ...(error.stack === undefined ? {} : { stack: error.stack }),
126
+ ...(source === undefined ? {} : { source }),
127
+ ...(line === undefined ? {} : { line }),
128
+ ...(droppedCount === undefined
129
+ ? {}
130
+ : { [CONSOLE_EVENT.DROPPED_PAGE_ERRORS]: droppedCount }),
131
+ },
132
+ });
133
+ };
134
+ // A page error must never break the connect path or the session that receives it.
135
+ const journalSafely = (session, error, droppedCount) => {
136
+ journal(session, error, droppedCount).catch(() => undefined);
137
+ };
138
+ return {
139
+ onPoolPageError(leaseId, error) {
140
+ const session = deps.resolveSession(leaseId);
141
+ if (session !== undefined) {
142
+ journalSafely(session, error);
143
+ return;
144
+ }
145
+ const entry = buffered.get(leaseId) ?? { errors: [], dropped: 0 };
146
+ if (entry.errors.length >= MAX_BUFFERED_PER_LEASE) {
147
+ entry.errors.shift();
148
+ entry.dropped += 1;
149
+ }
150
+ entry.errors.push(error);
151
+ buffered.set(leaseId, entry);
152
+ },
153
+ onSessionCreate(session) {
154
+ // Claim identities the SDK reports, so a page error the SDK already journaled adds no copy.
155
+ session.onEvent((event) => {
156
+ if (event.type !== EventType.ERROR_UNCAUGHT) {
157
+ return;
158
+ }
159
+ const message = event.data['message'];
160
+ if ('string' !== typeof message) {
161
+ return;
162
+ }
163
+ mark(session.id, uncaughtIdentity(message));
164
+ });
165
+ const leaseId = leaseParamOf(session.url);
166
+ if (leaseId === undefined) {
167
+ return;
168
+ }
169
+ const entry = buffered.get(leaseId);
170
+ if (entry === undefined) {
171
+ return;
172
+ }
173
+ buffered.delete(leaseId);
174
+ // The buffer is bounded, so a page that threw harder than the bound reports the loss on the
175
+ // first error that survived — the count names what the agent is not seeing.
176
+ entry.errors.forEach((error, i) => {
177
+ journalSafely(session, error, 0 === i && entry.dropped > 0 ? entry.dropped : undefined);
178
+ });
179
+ },
180
+ // A lease that is released or reaped will never flush: its session already ended, or its page
181
+ // died before any SDK connected. The buffer has nowhere to go, so it goes away.
182
+ onLeaseClosed(leaseId) {
183
+ buffered.delete(leaseId);
184
+ },
185
+ // The dedupe memory belongs to one session's lifetime; an ended session's identities would
186
+ // otherwise suppress nothing real and grow for the life of the daemon.
187
+ onSessionEnd(sessionId) {
188
+ seen.delete(sessionId);
189
+ },
190
+ onPoolDialog(leaseId, dialog) {
191
+ // An own key only: an inherited name such as `toString` is not a dialog kind.
192
+ const kind = dialog.kind;
193
+ const answer = kind !== undefined && Object.hasOwn(DISMISSED_ANSWER, kind)
194
+ ? DISMISSED_ANSWER[kind]
195
+ : undefined;
196
+ const session = deps.resolveSession(leaseId);
197
+ if (answer === undefined || session === undefined) {
198
+ return;
199
+ }
200
+ session.pushEvent({
201
+ t: 0, // pushEvent re-stamps with the session's own clock
202
+ type: EventType.DIALOG_OPENED,
203
+ sessionId: session.id,
204
+ data: { kind: dialog.kind, message: dialog.message, ...answer },
205
+ });
206
+ },
207
+ };
208
+ }