@reticlehq/server 3.8.2 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (477) hide show
  1. package/LICENSE +201 -104
  2. package/NOTICE +5 -0
  3. package/README.md +1 -1
  4. package/SKILL.md +13 -12
  5. package/bin/launch.js +13 -0
  6. package/bin/package.json +3 -0
  7. package/bin/reticle.js +66 -8
  8. package/dist/chromium-launch-options.d.ts +25 -0
  9. package/dist/chromium-launch-options.js +21 -1
  10. package/dist/command/cli/adhoc-suite.js +2 -0
  11. package/dist/command/cli/adhoc-verdict.d.ts +2 -43
  12. package/dist/command/cli/adhoc-verdict.js +50 -160
  13. package/dist/command/cli/answers/gate-exit.d.ts +6 -2
  14. package/dist/command/cli/answers/gate-exit.js +12 -2
  15. package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
  16. package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
  17. package/dist/command/cli/ci-hint.d.ts +44 -0
  18. package/dist/command/cli/ci-hint.js +72 -0
  19. package/dist/command/cli/cli-doctor.d.ts +6 -1
  20. package/dist/command/cli/cli-doctor.js +46 -5
  21. package/dist/command/cli/cli-flow-commands.d.ts +7 -1
  22. package/dist/command/cli/cli-flow-commands.js +39 -24
  23. package/dist/command/cli/cli-kill.d.ts +21 -0
  24. package/dist/command/cli/cli-kill.js +47 -4
  25. package/dist/command/cli/cli-parse-compare.d.ts +22 -0
  26. package/dist/command/cli/cli-parse-compare.js +75 -0
  27. package/dist/command/cli/cli-parse-grade.d.ts +36 -0
  28. package/dist/command/cli/cli-parse-grade.js +75 -0
  29. package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
  30. package/dist/command/cli/cli-parse-grammar.js +18 -20
  31. package/dist/command/cli/cli-parse-verify.d.ts +10 -0
  32. package/dist/command/cli/cli-parse-verify.js +63 -14
  33. package/dist/command/cli/cli-parse.d.ts +22 -17
  34. package/dist/command/cli/cli-parse.js +54 -32
  35. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  36. package/dist/command/cli/cli-update-commands.js +29 -20
  37. package/dist/command/cli/cli-usage.js +61 -31
  38. package/dist/command/cli/cli-verify.d.ts +17 -3
  39. package/dist/command/cli/cli-verify.js +59 -21
  40. package/dist/command/cli/cloud-cli.js +2 -119
  41. package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
  42. package/dist/command/cli/daemon-link/daemon-client.js +205 -0
  43. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +6 -1
  44. package/dist/command/cli/doctor/browser/chromium-hint.js +11 -5
  45. package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
  46. package/dist/command/cli/doctor/doctor-fix.d.ts +44 -0
  47. package/dist/command/cli/doctor/doctor-fix.js +89 -0
  48. package/dist/command/cli/doctor/doctor-launcher-line.d.ts +23 -0
  49. package/dist/command/cli/doctor/doctor-launcher-line.js +60 -0
  50. package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
  51. package/dist/command/cli/doctor/doctor-rows.js +4 -0
  52. package/dist/command/cli/doctor-repairs.d.ts +64 -0
  53. package/dist/command/cli/doctor-repairs.js +214 -0
  54. package/dist/command/cli/drive/drive-command.js +3 -7
  55. package/dist/command/cli/ensure-daemon.d.ts +26 -0
  56. package/dist/command/cli/ensure-daemon.js +57 -0
  57. package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
  58. package/dist/command/cli/finished-run/grade-command.js +180 -0
  59. package/dist/command/cli/first-use-checks.d.ts +41 -0
  60. package/dist/command/cli/first-use-checks.js +127 -0
  61. package/dist/command/cli/first-verdict-node.d.ts +3 -0
  62. package/dist/command/cli/first-verdict-node.js +189 -0
  63. package/dist/command/cli/first-verdict.d.ts +126 -0
  64. package/dist/command/cli/first-verdict.js +267 -0
  65. package/dist/command/cli/launch/sdk-version-match.d.ts +3 -2
  66. package/dist/command/cli/launch/sdk-version-match.js +3 -2
  67. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
  68. package/dist/command/cli/lifecycle/daemon-lifecycle.js +7 -6
  69. package/dist/command/cli/mcp-command.js +13 -4
  70. package/dist/command/cli/ports/port-holder.d.ts +11 -0
  71. package/dist/command/cli/ports/port-holder.js +36 -4
  72. package/dist/command/cli/setup-mcp-cli.d.ts +27 -1
  73. package/dist/command/cli/setup-mcp-cli.js +98 -23
  74. package/dist/command/cli/status/status-lines.d.ts +2 -0
  75. package/dist/command/cli/status/status-lines.js +72 -17
  76. package/dist/command/cli/status-fields.d.ts +5 -1
  77. package/dist/command/cli/status-fields.js +24 -6
  78. package/dist/command/cli/try-command.d.ts +7 -0
  79. package/dist/command/cli/try-command.js +11 -3
  80. package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
  81. package/dist/command/cli/two-builds/compare-command.js +314 -0
  82. package/dist/command/cli/verify-against.d.ts +61 -0
  83. package/dist/command/cli/verify-against.js +187 -0
  84. package/dist/command/cli/verify-no-session.d.ts +2 -0
  85. package/dist/command/cli/verify-no-session.js +13 -0
  86. package/dist/command/cli/verify-results.d.ts +35 -2
  87. package/dist/command/cli/verify-results.js +76 -10
  88. package/dist/command/cli.js +112 -70
  89. package/dist/command/daemon/binding/daemon-status-probe.d.ts +3 -1
  90. package/dist/command/daemon/binding/daemon-status-probe.js +4 -2
  91. package/dist/command/daemon/daemon-argv.d.ts +9 -0
  92. package/dist/command/daemon/daemon-argv.js +11 -0
  93. package/dist/command/daemon/daemon-resolve.d.ts +8 -1
  94. package/dist/command/daemon/daemon-resolve.js +27 -1
  95. package/dist/command/daemon/daemon.d.ts +22 -4
  96. package/dist/command/daemon/daemon.js +52 -10
  97. package/dist/command/daemon/lifetime/daemon-exit.d.ts +24 -0
  98. package/dist/command/daemon/lifetime/daemon-exit.js +39 -0
  99. package/dist/command/daemon/lifetime/death-cause.d.ts +15 -0
  100. package/dist/command/daemon/lifetime/death-cause.js +149 -0
  101. package/dist/command/daemon/lifetime/heartbeat.d.ts +7 -15
  102. package/dist/command/daemon/lifetime/heartbeat.js +11 -15
  103. package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
  104. package/dist/command/daemon/lifetime/startup-failure.js +62 -0
  105. package/dist/command/daemon/port-state.d.ts +14 -0
  106. package/dist/command/daemon/port-state.js +31 -0
  107. package/dist/command/setup/agent-configs.d.ts +2 -0
  108. package/dist/command/setup/agent-configs.js +42 -15
  109. package/dist/command/setup/agent-next-steps.d.ts +77 -0
  110. package/dist/command/setup/agent-next-steps.js +284 -0
  111. package/dist/command/setup/agent-writer.js +11 -6
  112. package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
  113. package/dist/command/setup/bringup/dev-server-files.js +17 -0
  114. package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
  115. package/dist/command/setup/bringup/static-page-server.js +43 -9
  116. package/dist/command/setup/init/init-host.js +2 -0
  117. package/dist/command/setup/init/init-runtime.d.ts +6 -1
  118. package/dist/command/setup/init/init-runtime.js +63 -21
  119. package/dist/command/setup/mcp-launcher.d.ts +3 -0
  120. package/dist/command/setup/mcp-launcher.js +11 -0
  121. package/dist/command/setup/node-effects.d.ts +3 -2
  122. package/dist/command/setup/node-effects.js +9 -3
  123. package/dist/command/setup/remaining-steps.d.ts +8 -0
  124. package/dist/command/setup/remaining-steps.js +10 -0
  125. package/dist/command/setup/run-setup.d.ts +2 -0
  126. package/dist/command/setup/run-setup.js +9 -1
  127. package/dist/command/setup/setup-command.js +36 -15
  128. package/dist/command/setup/setup-mcp.d.ts +8 -0
  129. package/dist/command/setup/setup-mcp.js +47 -20
  130. package/dist/command/setup/start-dev-server.d.ts +8 -0
  131. package/dist/command/setup/start-dev-server.js +43 -0
  132. package/dist/command/update/updater.js +6 -0
  133. package/dist/command/version/identity/server-version.d.ts +2 -0
  134. package/dist/command/version/identity/server-version.js +2 -0
  135. package/dist/command/version/version-skew.d.ts +7 -0
  136. package/dist/command/version/version-skew.js +14 -3
  137. package/dist/features/compare/action-plan.d.ts +260 -0
  138. package/dist/features/compare/action-plan.js +543 -0
  139. package/dist/features/compare/behaviour-compare.d.ts +24 -0
  140. package/dist/features/compare/behaviour-compare.js +85 -0
  141. package/dist/features/compare/behaviour-diff.d.ts +97 -0
  142. package/dist/features/compare/behaviour-diff.js +202 -0
  143. package/dist/features/compare/behaviour-report.d.ts +40 -0
  144. package/dist/features/compare/behaviour-report.js +128 -0
  145. package/dist/features/compare/build-readings.d.ts +106 -0
  146. package/dist/features/compare/build-readings.js +250 -0
  147. package/dist/features/compare/compare-diff.d.ts +62 -0
  148. package/dist/features/compare/compare-diff.js +177 -0
  149. package/dist/features/compare/compare-report-html.d.ts +6 -0
  150. package/dist/features/compare/compare-report-html.js +102 -0
  151. package/dist/features/compare/compare-report.d.ts +15 -0
  152. package/dist/features/compare/compare-report.js +73 -0
  153. package/dist/features/compare/compare-run.d.ts +60 -0
  154. package/dist/features/compare/compare-run.js +123 -0
  155. package/dist/features/compare/outside/effect-lines.d.ts +26 -0
  156. package/dist/features/compare/outside/effect-lines.js +103 -0
  157. package/dist/features/compare/outside/state-sources.d.ts +76 -0
  158. package/dist/features/compare/outside/state-sources.js +164 -0
  159. package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
  160. package/dist/features/compare/outside/value-dictionary.js +227 -0
  161. package/dist/features/compare/visible-content.d.ts +20 -0
  162. package/dist/features/compare/visible-content.js +125 -0
  163. package/dist/features/crawl/crawl-empty.js +2 -1
  164. package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
  165. package/dist/features/crawl/crawl-exhaustive.js +138 -0
  166. package/dist/features/crawl/crawl-tools.d.ts +9 -2
  167. package/dist/features/crawl/crawl-tools.js +28 -12
  168. package/dist/features/crawl/crawl.d.ts +8 -9
  169. package/dist/features/crawl/crawl.js +61 -65
  170. package/dist/features/exhaust/app-coverage.d.ts +5 -1
  171. package/dist/features/exhaust/app-coverage.js +3 -1
  172. package/dist/features/exhaust/code-coverage.js +8 -4
  173. package/dist/features/exhaust/edge-cases.d.ts +133 -0
  174. package/dist/features/exhaust/edge-cases.js +310 -0
  175. package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
  176. package/dist/features/exhaust/edges/edge-drive.js +364 -0
  177. package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
  178. package/dist/features/exhaust/edges/edge-run.js +169 -0
  179. package/dist/features/exhaust/explorer.d.ts +87 -14
  180. package/dist/features/exhaust/explorer.js +159 -25
  181. package/dist/features/exhaust/failure-branches.d.ts +5 -0
  182. package/dist/features/exhaust/failure-branches.js +29 -11
  183. package/dist/features/exhaust/invariants.d.ts +112 -0
  184. package/dist/features/exhaust/invariants.js +188 -0
  185. package/dist/features/exhaust/ledger.d.ts +68 -0
  186. package/dist/features/exhaust/ledger.js +121 -13
  187. package/dist/features/exhaust/session-fold.d.ts +10 -0
  188. package/dist/features/exhaust/session-fold.js +46 -3
  189. package/dist/features/exhaust/session-port.d.ts +3 -1
  190. package/dist/features/exhaust/session-port.js +97 -28
  191. package/dist/features/grade/grade-answer.d.ts +99 -0
  192. package/dist/features/grade/grade-answer.js +112 -0
  193. package/dist/features/grade/grade-noise.d.ts +27 -0
  194. package/dist/features/grade/grade-noise.js +55 -0
  195. package/dist/features/grade/grade-renumber.d.ts +12 -0
  196. package/dist/features/grade/grade-renumber.js +86 -0
  197. package/dist/features/grade/grade-state.d.ts +50 -0
  198. package/dist/features/grade/grade-state.js +93 -0
  199. package/dist/features/grade/grade-suggest.d.ts +33 -0
  200. package/dist/features/grade/grade-suggest.js +98 -0
  201. package/dist/features/license/license.d.ts +11 -71
  202. package/dist/features/license/license.js +43 -136
  203. package/dist/features/visual/desktop-capture.d.ts +33 -0
  204. package/dist/features/visual/desktop-capture.js +94 -0
  205. package/dist/features/visual/step-shots.js +139 -35
  206. package/dist/features/visual/visual-tools.d.ts +0 -18
  207. package/dist/features/visual/visual-tools.js +67 -99
  208. package/dist/import-graph.js +5 -6
  209. package/dist/index.d.ts +3 -1
  210. package/dist/index.js +19 -7
  211. package/dist/judgement/capsule/causal-summary.js +9 -2
  212. package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
  213. package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
  214. package/dist/judgement/runs/reticle-runner.js +16 -2
  215. package/dist/judgement/runs/run-ledger.d.ts +7 -5
  216. package/dist/judgement/runs/run-ledger.js +98 -69
  217. package/dist/judgement/runs/runner-port.d.ts +2 -0
  218. package/dist/judgement/runs/runner-port.js +23 -5
  219. package/dist/judgement/runs/verification-sync.d.ts +6 -3
  220. package/dist/judgement/runs/verification-sync.js +21 -10
  221. package/dist/language/flows/affected-tools.d.ts +1 -1
  222. package/dist/language/flows/affected-tools.js +49 -30
  223. package/dist/language/flows/change/git-changed.d.ts +2 -1
  224. package/dist/language/flows/change/git-changed.js +19 -2
  225. package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
  226. package/dist/language/flows/fields/flow-secret-field.js +77 -21
  227. package/dist/language/flows/flow-anchor.d.ts +60 -2
  228. package/dist/language/flows/flow-anchor.js +125 -20
  229. package/dist/language/flows/flow-classify.d.ts +20 -0
  230. package/dist/language/flows/flow-classify.js +90 -12
  231. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  232. package/dist/language/flows/flow-cross-step.js +25 -1
  233. package/dist/language/flows/flow-journey.d.ts +38 -4
  234. package/dist/language/flows/flow-journey.js +138 -8
  235. package/dist/language/flows/flow-replay-run.d.ts +0 -16
  236. package/dist/language/flows/flow-replay-run.js +76 -14
  237. package/dist/language/flows/flow-replay-types.d.ts +4 -2
  238. package/dist/language/flows/flow-replay.d.ts +19 -1
  239. package/dist/language/flows/flow-replay.js +169 -52
  240. package/dist/language/flows/flow-replayable.js +7 -1
  241. package/dist/language/flows/flow-start-path.d.ts +34 -5
  242. package/dist/language/flows/flow-start-path.js +202 -15
  243. package/dist/language/flows/flow-step-runners.d.ts +14 -13
  244. package/dist/language/flows/flow-step-runners.js +184 -109
  245. package/dist/language/flows/flow-store-for-session.d.ts +26 -25
  246. package/dist/language/flows/flow-store-for-session.js +51 -5
  247. package/dist/language/flows/flow-tools.js +46 -33
  248. package/dist/language/flows/flows.js +176 -76
  249. package/dist/language/flows/recording/tape/recordings.d.ts +21 -16
  250. package/dist/language/flows/recording/tape/recordings.js +112 -49
  251. package/dist/language/flows/replay.d.ts +36 -12
  252. package/dist/language/flows/replay.js +193 -50
  253. package/dist/language/flows/steps/page-steps.d.ts +68 -0
  254. package/dist/language/flows/steps/page-steps.js +198 -0
  255. package/dist/language/flows/suite/sequential-run.d.ts +68 -0
  256. package/dist/language/flows/suite/sequential-run.js +96 -0
  257. package/dist/language/flows/verify-change-tools.js +26 -15
  258. package/dist/launch-chromium.d.ts +1 -0
  259. package/dist/launch-chromium.js +49 -12
  260. package/dist/memory/cloud/background-sync.d.ts +13 -0
  261. package/dist/memory/cloud/background-sync.js +24 -3
  262. package/dist/memory/cloud/sync-daemon.js +26 -27
  263. package/dist/memory/cloud/sync-screenshots.js +6 -1
  264. package/dist/memory/journal/attach-journal.js +2 -1
  265. package/dist/memory/journal/drive-flow.d.ts +7 -0
  266. package/dist/memory/journal/drive-flow.js +44 -13
  267. package/dist/memory/journal/journal-recorder.d.ts +4 -2
  268. package/dist/memory/journal/session-journal.d.ts +7 -0
  269. package/dist/memory/journal/session-journal.js +58 -10
  270. package/dist/memory/project/root-claims.d.ts +9 -0
  271. package/dist/memory/project/root-claims.js +9 -4
  272. package/dist/portal/bridge/bridge.d.ts +13 -4
  273. package/dist/portal/bridge/bridge.js +10 -9
  274. package/dist/portal/input/download-capture.d.ts +7 -0
  275. package/dist/portal/input/download-capture.js +92 -0
  276. package/dist/portal/input/network-detail.d.ts +6 -0
  277. package/dist/portal/input/network-detail.js +12 -1
  278. package/dist/portal/input/network-mock-tools.js +49 -13
  279. package/dist/portal/input/network-mock.d.ts +8 -0
  280. package/dist/portal/input/network-mock.js +30 -10
  281. package/dist/portal/input/page-capture.d.ts +24 -0
  282. package/dist/portal/input/page-capture.js +33 -0
  283. package/dist/portal/input/real-input.d.ts +35 -18
  284. package/dist/portal/input/real-input.js +99 -33
  285. package/dist/portal/input/viewport-tools.d.ts +8 -0
  286. package/dist/portal/input/viewport-tools.js +81 -21
  287. package/dist/portal/pool/browser-pool.d.ts +16 -30
  288. package/dist/portal/pool/browser-pool.js +64 -52
  289. package/dist/portal/pool/context-permissions.d.ts +34 -17
  290. package/dist/portal/pool/context-permissions.js +94 -1
  291. package/dist/portal/pool/csp-widen.d.ts +62 -0
  292. package/dist/portal/pool/csp-widen.js +132 -0
  293. package/dist/portal/pool/determinism.d.ts +52 -0
  294. package/dist/portal/pool/determinism.js +259 -0
  295. package/dist/portal/pool/lease-emulation.d.ts +34 -1
  296. package/dist/portal/pool/lease-emulation.js +18 -0
  297. package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
  298. package/dist/portal/pool/lease-marker-stamp.js +15 -1
  299. package/dist/portal/pool/lease-page-error.d.ts +56 -0
  300. package/dist/portal/pool/lease-page-error.js +160 -0
  301. package/dist/portal/pool/page-error-journal.d.ts +63 -0
  302. package/dist/portal/pool/page-error-journal.js +208 -0
  303. package/dist/portal/pool/playwright-launcher.js +71 -15
  304. package/dist/portal/pool/pool-contract.d.ts +49 -1
  305. package/dist/portal/pool/pool-lease.d.ts +70 -2
  306. package/dist/portal/pool/pool-lease.js +23 -0
  307. package/dist/portal/pool/zero-install.d.ts +8 -0
  308. package/dist/portal/pool/zero-install.js +91 -1
  309. package/dist/portal/session/dev-server/dev-command.d.ts +11 -2
  310. package/dist/portal/session/dev-server/dev-command.js +110 -16
  311. package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
  312. package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
  313. package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
  314. package/dist/portal/session/dev-server/project-ports.js +62 -0
  315. package/dist/portal/session/facts/departed-network.d.ts +66 -0
  316. package/dist/portal/session/facts/departed-network.js +142 -0
  317. package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
  318. package/dist/portal/session/no-session-diagnosis.js +76 -27
  319. package/dist/portal/session/no-session-next-action.d.ts +14 -0
  320. package/dist/portal/session/no-session-next-action.js +56 -7
  321. package/dist/portal/session/no-session-watch.d.ts +6 -0
  322. package/dist/portal/session/no-session-watch.js +41 -4
  323. package/dist/portal/session/page-commands/undeclared-command.d.ts +9 -3
  324. package/dist/portal/session/page-commands/undeclared-command.js +16 -7
  325. package/dist/portal/session/recorded-gaps.d.ts +1 -1
  326. package/dist/portal/session/recorded-gaps.js +5 -2
  327. package/dist/portal/session/serving-directory.d.ts +10 -0
  328. package/dist/portal/session/serving-directory.js +39 -8
  329. package/dist/portal/session/session-manager.d.ts +10 -0
  330. package/dist/portal/session/session-manager.js +211 -81
  331. package/dist/portal/session/session.js +4 -3
  332. package/dist/portal/session/zero-install-first-call.d.ts +20 -1
  333. package/dist/portal/session/zero-install-first-call.js +34 -2
  334. package/dist/start-wiring.d.ts +30 -2
  335. package/dist/start-wiring.js +74 -9
  336. package/dist/surface/mcp/mcp-proxy.js +10 -2
  337. package/dist/surface/mcp/mcp.js +8 -5
  338. package/dist/surface/mcp/peer/agents-connected.d.ts +6 -0
  339. package/dist/surface/mcp/peer/agents-connected.js +58 -0
  340. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +17 -5
  341. package/dist/surface/mcp/shared-params.d.ts +2 -0
  342. package/dist/surface/mcp/shared-params.js +31 -0
  343. package/dist/surface/tools/act/act-observation.d.ts +40 -2
  344. package/dist/surface/tools/act/act-observation.js +118 -12
  345. package/dist/surface/tools/act/act-preflight.js +38 -18
  346. package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
  347. package/dist/surface/tools/act/act-sequence-retry.js +61 -5
  348. package/dist/surface/tools/act/act-target.js +3 -1
  349. package/dist/surface/tools/act/after-match.d.ts +10 -0
  350. package/dist/surface/tools/act/after-match.js +40 -0
  351. package/dist/surface/tools/act/already-true.js +1 -1
  352. package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
  353. package/dist/surface/tools/act/navigation/reload-result.js +25 -2
  354. package/dist/surface/tools/act/resolve-target.d.ts +17 -1
  355. package/dist/surface/tools/act/resolve-target.js +59 -9
  356. package/dist/surface/tools/act/sequence-grade.d.ts +15 -1
  357. package/dist/surface/tools/act/sequence-grade.js +45 -27
  358. package/dist/surface/tools/act/settle-in-flight.js +27 -15
  359. package/dist/surface/tools/act-command.d.ts +23 -0
  360. package/dist/surface/tools/act-command.js +50 -0
  361. package/dist/surface/tools/act-sequence-tool.js +81 -50
  362. package/dist/surface/tools/act-tools.d.ts +1 -13
  363. package/dist/surface/tools/act-tools.js +83 -95
  364. package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
  365. package/dist/surface/tools/agent/build-staleness.js +1 -1
  366. package/dist/surface/tools/assert/assert-verdict.js +18 -12
  367. package/dist/surface/tools/browser-tools.js +87 -13
  368. package/dist/surface/tools/compare-tools.d.ts +11 -0
  369. package/dist/surface/tools/compare-tools.js +94 -0
  370. package/dist/surface/tools/coverage-tools.js +1 -1
  371. package/dist/surface/tools/dynamic-tools.js +11 -3
  372. package/dist/surface/tools/empty-session-list.d.ts +2 -0
  373. package/dist/surface/tools/empty-session-list.js +4 -2
  374. package/dist/surface/tools/error-recovery.d.ts +12 -1
  375. package/dist/surface/tools/error-recovery.js +131 -40
  376. package/dist/surface/tools/explore-tools.js +1 -1
  377. package/dist/surface/tools/invoke-tool.js +29 -21
  378. package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
  379. package/dist/surface/tools/lease-coverage-tools.js +128 -0
  380. package/dist/surface/tools/lease-hint.d.ts +42 -1
  381. package/dist/surface/tools/lease-hint.js +52 -3
  382. package/dist/surface/tools/lease-readiness.d.ts +47 -0
  383. package/dist/surface/tools/lease-readiness.js +126 -0
  384. package/dist/surface/tools/lease-seed.d.ts +16 -1
  385. package/dist/surface/tools/lease-seed.js +37 -4
  386. package/dist/surface/tools/lease-tools.d.ts +3 -41
  387. package/dist/surface/tools/lease-tools.js +104 -124
  388. package/dist/surface/tools/lease-url-identity.d.ts +46 -0
  389. package/dist/surface/tools/lease-url-identity.js +97 -0
  390. package/dist/surface/tools/merge-tools.js +99 -2
  391. package/dist/surface/tools/merged-name-redirect.js +5 -1
  392. package/dist/surface/tools/observe-tools.js +15 -9
  393. package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
  394. package/dist/surface/tools/read/console-pre-attach.js +23 -0
  395. package/dist/surface/tools/read-tools.js +33 -30
  396. package/dist/surface/tools/real-input-attempt.d.ts +0 -8
  397. package/dist/surface/tools/real-input-attempt.js +143 -28
  398. package/dist/surface/tools/repeat-hint.d.ts +40 -0
  399. package/dist/surface/tools/repeat-hint.js +171 -0
  400. package/dist/surface/tools/run-report.d.ts +2 -2
  401. package/dist/surface/tools/run-report.js +27 -15
  402. package/dist/surface/tools/session-ports.d.ts +14 -0
  403. package/dist/surface/tools/session-ports.js +53 -0
  404. package/dist/surface/tools/tool-kit.d.ts +18 -0
  405. package/dist/surface/tools/tool-kit.js +9 -3
  406. package/dist/surface/tools/tool-surface.js +0 -1
  407. package/dist/surface/tools/tools.js +54 -18
  408. package/dist/surface/tools/type-spelling.d.ts +13 -0
  409. package/dist/surface/tools/type-spelling.js +36 -0
  410. package/dist/telemetry/agent-client.d.ts +6 -0
  411. package/dist/telemetry/agent-client.js +10 -0
  412. package/dist/telemetry/argument-shape.js +17 -7
  413. package/dist/telemetry/cli-telemetry.d.ts +10 -1
  414. package/dist/telemetry/cli-telemetry.js +15 -0
  415. package/dist/telemetry/init-telemetry.d.ts +2 -1
  416. package/dist/telemetry/init-telemetry.js +47 -2
  417. package/dist/telemetry/install-trace.js +23 -4
  418. package/dist/telemetry/onboarding-funnel.d.ts +19 -2
  419. package/dist/telemetry/onboarding-funnel.js +25 -3
  420. package/dist/telemetry/proxy-telemetry.d.ts +2 -1
  421. package/dist/telemetry/proxy-telemetry.js +2 -2
  422. package/dist/telemetry/session-metrics.d.ts +1 -1
  423. package/dist/telemetry/session-metrics.js +67 -27
  424. package/dist/telemetry/telemetry.d.ts +3 -1
  425. package/dist/telemetry/telemetry.js +60 -28
  426. package/dist/telemetry/tool-refused.d.ts +6 -3
  427. package/dist/telemetry/tool-refused.js +15 -3
  428. package/docs/actions.mdx +3 -3
  429. package/docs/agent-cheatsheet.md +1 -1
  430. package/docs/architecture.md +3 -6
  431. package/docs/capabilities.mdx +61 -1
  432. package/docs/cli/affected.mdx +9 -7
  433. package/docs/cli/cloud.mdx +3 -3
  434. package/docs/cli/compare.mdx +94 -0
  435. package/docs/cli/doctor.mdx +41 -2
  436. package/docs/cli/grade.mdx +97 -0
  437. package/docs/cli/help.mdx +4 -2
  438. package/docs/cli/license.mdx +13 -12
  439. package/docs/cli/restart.mdx +7 -5
  440. package/docs/cli/rollback.mdx +5 -5
  441. package/docs/cli/setup.mdx +13 -2
  442. package/docs/cli/status.mdx +4 -0
  443. package/docs/cli/stop.mdx +3 -3
  444. package/docs/cli/update.mdx +2 -2
  445. package/docs/cli/verify.mdx +23 -1
  446. package/docs/cli.mdx +34 -24
  447. package/docs/deploy-checks.md +25 -0
  448. package/docs/desktop.mdx +1 -1
  449. package/docs/enterprise.md +17 -22
  450. package/docs/environment.mdx +1 -0
  451. package/docs/first-drive.md +1 -1
  452. package/docs/flows.md +11 -1
  453. package/docs/frameworks.mdx +9 -9
  454. package/docs/getting-started.md +1 -1
  455. package/docs/install-manual.mdx +16 -1
  456. package/docs/license-activation.mdx +3 -3
  457. package/docs/packages/engine.mdx +1 -1
  458. package/docs/packages/server.mdx +1 -1
  459. package/docs/packages/tauri.mdx +1 -1
  460. package/docs/packages/test.mdx +1 -1
  461. package/docs/packages.mdx +1 -1
  462. package/docs/platform-integration.md +1 -1
  463. package/docs/predicates.mdx +1 -1
  464. package/docs/quickstart.mdx +68 -49
  465. package/docs/skill-setup.md +1 -1
  466. package/docs/skill.md +14 -13
  467. package/docs/telemetry.md +6 -4
  468. package/docs/tools/navigate.mdx +1 -1
  469. package/docs/tools/verify.mdx +4 -1
  470. package/docs/troubleshooting.mdx +1 -1
  471. package/docs/usage.md +17 -14
  472. package/package.json +18 -14
  473. package/LICENSE-ENTERPRISE +0 -37
  474. package/dist/features/ee/audit-log.d.ts +0 -14
  475. package/dist/features/ee/audit-log.js +0 -11
  476. package/dist/features/exhaust/exhaust-run.d.ts +0 -33
  477. package/dist/features/exhaust/exhaust-run.js +0 -82
@@ -8,9 +8,9 @@
8
8
  *
9
9
  * Pure apart from one injected reader, so every case is a unit test rather than a temp directory.
10
10
  */
11
- import { readFileSync, readdirSync } from 'node:fs';
11
+ import { accessSync, constants, readFileSync, readdirSync } from 'node:fs';
12
12
  import { join } from 'node:path';
13
- import { RETICLE_NPM_PACKAGE, desktopLaunch, workspacePackageDirs } from '@reticlehq/init';
13
+ import { RETICLE_NPM_PACKAGE, desktopLaunch, isWebApp, workspacePackageDirs, } from '@reticlehq/init';
14
14
  /**
15
15
  * Script names checked, in order.
16
16
  *
@@ -36,6 +36,7 @@ const YARN_DLX = 'yarn dlx';
36
36
  const INIT_VERB = 'init';
37
37
  const DEFAULT_PACKAGE_MANAGER = 'npm';
38
38
  const PACKAGE_JSON = 'package.json';
39
+ const PNPM_WORKSPACE = 'pnpm-workspace.yaml';
39
40
  /** `--port 4311` / `--port=4311`, the flag every dev server in this list accepts. */
40
41
  const PORT_FLAG = /--port[= ]\s*(\d+)/;
41
42
  /**
@@ -59,15 +60,17 @@ function readTextFile(path) {
59
60
  function pinnedPort(script) {
60
61
  const match = PORT_FLAG.exec(script) ?? PORT_SHORT_FLAG.exec(script) ?? PORT_ENV.exec(script);
61
62
  const digits = match?.[1];
62
- if (digits === undefined)
63
+ if (digits === undefined) {
63
64
  return undefined;
65
+ }
64
66
  const port = Number.parseInt(digits, 10);
65
67
  return Number.isSafeInteger(port) ? port : undefined;
66
68
  }
67
69
  /** The parsed package.json, or undefined for anything unreadable/unparseable. */
68
70
  function readManifest(raw) {
69
- if (raw === undefined)
71
+ if (raw === undefined) {
70
72
  return undefined;
73
+ }
71
74
  try {
72
75
  return JSON.parse(raw);
73
76
  }
@@ -77,11 +80,13 @@ function readManifest(raw) {
77
80
  }
78
81
  /** The scripts block of a parsed package.json, or an empty one. */
79
82
  function scriptsOf(manifest) {
80
- if (null === manifest || 'object' !== typeof manifest)
83
+ if (null === manifest || 'object' !== typeof manifest) {
81
84
  return {};
85
+ }
82
86
  const scripts = manifest.scripts;
83
- if (null === scripts || 'object' !== typeof scripts)
87
+ if (null === scripts || 'object' !== typeof scripts) {
84
88
  return {};
89
+ }
85
90
  return scripts;
86
91
  }
87
92
  /** The package manager the lockfiles under `directory` identify. Anything else is npm. */
@@ -98,8 +103,9 @@ function devCommandFor(directory, manager, read) {
98
103
  // spawns are the same.
99
104
  const desktop = desktopLaunch(manifest);
100
105
  if (desktop !== undefined) {
101
- if ('command' in desktop)
106
+ if ('command' in desktop) {
102
107
  return { command: desktop.command };
108
+ }
103
109
  const command = `${manager} run ${desktop.script}`;
104
110
  return {
105
111
  command: 0 < desktop.args.length ? `${command} ${desktop.args}` : command,
@@ -108,8 +114,9 @@ function devCommandFor(directory, manager, read) {
108
114
  }
109
115
  for (const script of DEV_SCRIPTS) {
110
116
  const body = scripts[script];
111
- if ('string' !== typeof body || 0 === body.trim().length)
117
+ if ('string' !== typeof body || 0 === body.trim().length) {
112
118
  continue;
119
+ }
113
120
  const port = pinnedPort(body);
114
121
  return {
115
122
  command: `${manager} run ${script}`,
@@ -119,6 +126,70 @@ function devCommandFor(directory, manager, read) {
119
126
  }
120
127
  return undefined;
121
128
  }
129
+ /** Folders never read as an app: dependencies, and anything hidden (`.git`, `.cache`). */
130
+ const NODE_MODULES = 'node_modules';
131
+ const HIDDEN_PREFIX = '.';
132
+ /** Whether the process can write in `path`. Asked of every web-app candidate — see `preferWebApps`. */
133
+ function isWritableDirectory(path) {
134
+ try {
135
+ accessSync(path, constants.W_OK);
136
+ return true;
137
+ }
138
+ catch {
139
+ return false;
140
+ }
141
+ }
142
+ /**
143
+ * Whether the package in `directory` is a web app, using init's own signal.
144
+ *
145
+ * A backend can have a dev script too, so the script alone is not enough. init already answers
146
+ * "is this a page somebody can open", and a second answer here would let the two disagree.
147
+ */
148
+ function isWebAppAt(directory, read) {
149
+ return isWebApp('.', {
150
+ exists: (path) => read(join(directory, path)) !== undefined,
151
+ readFile: (path) => read(join(directory, path)) ?? null,
152
+ });
153
+ }
154
+ /**
155
+ * Web apps first, and of those the ones this process can write. When nothing here reads as a web
156
+ * app, every candidate comes back, in its original order.
157
+ *
158
+ * `isWebApp` is a detector, and a detector has gaps: a custom webpack or esbuild setup, a framework
159
+ * it does not know yet, a repository whose only candidate is a backend. Gating on it — as this code
160
+ * first did — hands the agent no next action where `main` hands it a command, and silence is the
161
+ * worse failure: a command that starts the wrong thing can be corrected, no command cannot. So the
162
+ * web-app check RANKS candidates instead of gating them.
163
+ *
164
+ * Writability is the exception: asked of the web apps only, where it is absolute. The reported bug
165
+ * (#2017) is a read-only backup copy sitting beside the real app, and a folder nobody can write must
166
+ * never be the one chosen while a writable sibling exists.
167
+ */
168
+ function preferWebApps(candidates, isWebAppCandidate, isWritable) {
169
+ const apps = candidates.filter(isWebAppCandidate);
170
+ return 0 < apps.length ? apps.filter(isWritable) : candidates;
171
+ }
172
+ /**
173
+ * The direct child folders of `directory` that are apps with a dev command, sorted by name: the web
174
+ * apps when there are any, every dev-script folder when there are none (see `preferWebApps`).
175
+ *
176
+ * Only for a folder with no package.json of its own. A task folder holds the app one level down, and
177
+ * no workspace file lists it, so `workspacePackageDirs` never finds it. `node_modules`, hidden
178
+ * folders and folders with no dev script are never candidates.
179
+ */
180
+ export function childDevApps(directory, read = readTextFile, listDirs = readDirNames, writable = isWritableDirectory) {
181
+ if (read(join(directory, PACKAGE_JSON)) !== undefined) {
182
+ return [];
183
+ }
184
+ const candidates = listDirs(directory)
185
+ .filter((name) => NODE_MODULES !== name && !name.startsWith(HIDDEN_PREFIX))
186
+ .filter((name) => {
187
+ const child = join(directory, name);
188
+ return devCommandFor(child, packageManager(child, read), read) !== undefined;
189
+ })
190
+ .sort();
191
+ return preferWebApps(candidates, (name) => isWebAppAt(join(directory, name), read), (name) => writable(join(directory, name)));
192
+ }
122
193
  /** Directory names under `path`, or none when it cannot be read. */
123
194
  function readDirNames(path) {
124
195
  try {
@@ -148,21 +219,44 @@ export function detectDevCommand(directory, read = readTextFile) {
148
219
  * init's own workspace listing answers which directories to consider; the root's lockfile picks the
149
220
  * package manager, because a workspace package usually has no lockfile of its own.
150
221
  */
151
- export function detectDevCommandInProject(directory, read = readTextFile, listDirs = readDirNames) {
222
+ export function detectDevCommandInProject(directory, read = readTextFile, listDirs = readDirNames, writable = isWritableDirectory) {
152
223
  const manager = packageManager(directory, read);
153
224
  const here = devCommandFor(directory, manager, read);
154
- if (here !== undefined)
225
+ if (here !== undefined) {
155
226
  return here;
227
+ }
228
+ // No package.json and no pnpm workspace file: a task folder, with the app one level down. With two
229
+ // apps there, choosing the first would start the wrong one; the caller names them through
230
+ // `childDevApps`. With none, the workspace search below still looks a level deeper.
231
+ if (read(join(directory, PACKAGE_JSON)) === undefined &&
232
+ read(join(directory, PNPM_WORKSPACE)) === undefined) {
233
+ const [only, ...others] = childDevApps(directory, read, listDirs, writable);
234
+ if (0 < others.length) {
235
+ return undefined;
236
+ }
237
+ if (only !== undefined) {
238
+ const child = join(directory, only);
239
+ const found = devCommandFor(child, packageManager(child, read), read);
240
+ if (found !== undefined) {
241
+ return { ...found, directory: only };
242
+ }
243
+ }
244
+ }
156
245
  const packages = workspacePackageDirs({
157
246
  readFile: (path) => read(join(directory, path)) ?? null,
158
247
  listDirs: (path) => listDirs(join(directory, path)),
159
248
  });
249
+ const candidates = [];
160
250
  for (const sub of packages) {
161
- const found = devCommandFor(join(directory, sub), manager, read);
162
- if (found !== undefined)
163
- return { ...found, directory: sub };
251
+ const child = join(directory, sub);
252
+ const command = devCommandFor(child, manager, read);
253
+ if (command !== undefined) {
254
+ candidates.push({ sub, child, command });
255
+ }
164
256
  }
165
- return undefined;
257
+ // Same rule as the child folders above, so a workspace and a task folder cannot disagree.
258
+ const [first] = preferWebApps(candidates, (entry) => isWebAppAt(entry.child, read), (entry) => writable(entry.child));
259
+ return first === undefined ? undefined : { ...first.command, directory: first.sub };
166
260
  }
167
261
  /**
168
262
  * The command that wires THIS project for good: `init`, in the app's own folder, through the
@@ -172,11 +266,11 @@ export function detectDevCommandInProject(directory, read = readTextFile, listDi
172
266
  * time: `init` run in a folder that is not the app stops on a missing package.json, and a runner the
173
267
  * machine does not have is "command not found". npx whenever there is doubt — it ships with Node.
174
268
  */
175
- export function initCommandIn(directory, read = readTextFile, listDirs = readDirNames) {
269
+ export function initCommandIn(directory, read = readTextFile, listDirs = readDirNames, writable = isWritableDirectory) {
176
270
  const manager = packageManager(directory, read);
177
271
  const berry = read(join(directory, YARN_BERRY_MARKER)) !== undefined;
178
272
  const runner = YARN === manager ? (berry ? YARN_DLX : NPX) : (RUNNER[manager] ?? NPX);
179
273
  const command = `${runner} ${RETICLE_NPM_PACKAGE} ${INIT_VERB}`;
180
- const app = detectDevCommandInProject(directory, read, listDirs)?.directory;
274
+ const app = detectDevCommandInProject(directory, read, listDirs, writable)?.directory;
181
275
  return app === undefined ? command : `cd ${app} && ${command}`;
182
276
  }
@@ -69,6 +69,13 @@ export declare function probeDevServerStates(ports?: readonly number[], probe?:
69
69
  serving: number[];
70
70
  slow: number[];
71
71
  }>;
72
+ /**
73
+ * Whether anything accepted a connection on this port, on either loopback family. Never rejects.
74
+ *
75
+ * A server compiling its first route answers `CONNECTED_NO_ANSWER`, and that counts: the lease uses
76
+ * this to stop telling an agent to start a dev server that is already running (#2005).
77
+ */
78
+ export declare function portAcceptsConnection(port: number, probe?: (port: number) => Promise<PortState>): Promise<boolean>;
72
79
  /** Every candidate port serving a document, in ascending order. Never rejects. */
73
80
  export declare function probeDevServers(ports?: readonly number[], probe?: (port: number) => Promise<boolean>): Promise<number[]>;
74
81
  export {};
@@ -156,6 +156,15 @@ async function anyFamilyState(port, probe = probeState) {
156
156
  return PortState.CONNECTED_NO_ANSWER;
157
157
  return PortState.CLOSED;
158
158
  }
159
+ /**
160
+ * Whether anything accepted a connection on this port, on either loopback family. Never rejects.
161
+ *
162
+ * A server compiling its first route answers `CONNECTED_NO_ANSWER`, and that counts: the lease uses
163
+ * this to stop telling an agent to start a dev server that is already running (#2005).
164
+ */
165
+ export async function portAcceptsConnection(port, probe = (p) => anyFamilyState(p)) {
166
+ return PortState.CLOSED !== (await classifyPort(port, probe));
167
+ }
159
168
  /** Every candidate port serving a document, in ascending order. Never rejects. */
160
169
  export async function probeDevServers(ports = [...DEV_SERVER_PORTS], probe = (p) => anyFamilyServes(p)) {
161
170
  const results = await Promise.all(ports.map((p) => probe(p).then((up) => (up ? p : null))));
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Ports this project sets in its `.env` files (reticle#1543).
3
+ *
4
+ * The no-session scan probes a list of well-known ports, the ports the build plugins announced, a
5
+ * port pinned in the `dev` script (#1367), and the ports of earlier sessions. An app that reads its
6
+ * port from `.env` is on none of those. Reported from React apps on Windows: a preview server on
7
+ * :3020, started by a Node script, and the answer said no app was running and suggested a dev
8
+ * server, which would start a duplicate.
9
+ *
10
+ * This only ADDS candidates to the scan. The scan still decides which of them answer like a dev
11
+ * server.
12
+ */
13
+ /** The `.env` files a Node dev setup reads, in the order dotenv-style loaders read them. */
14
+ export declare const ENV_PORT_FILES: readonly [".env", ".env.local", ".env.development", ".env.development.local"];
15
+ /**
16
+ * The ports the project's `.env` files set with `PORT=`.
17
+ *
18
+ * Every file is read, not only the one a given loader would pick: the scan only probes candidates,
19
+ * so an extra port costs one connection attempt, while a missed one is the defect this fixes.
20
+ */
21
+ export declare function envFilePorts(directory: string, read?: (path: string) => string | undefined): number[];
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Ports this project sets in its `.env` files (reticle#1543).
3
+ *
4
+ * The no-session scan probes a list of well-known ports, the ports the build plugins announced, a
5
+ * port pinned in the `dev` script (#1367), and the ports of earlier sessions. An app that reads its
6
+ * port from `.env` is on none of those. Reported from React apps on Windows: a preview server on
7
+ * :3020, started by a Node script, and the answer said no app was running and suggested a dev
8
+ * server, which would start a duplicate.
9
+ *
10
+ * This only ADDS candidates to the scan. The scan still decides which of them answer like a dev
11
+ * server.
12
+ */
13
+ import { readFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ /** The `.env` files a Node dev setup reads, in the order dotenv-style loaders read them. */
16
+ export const ENV_PORT_FILES = [
17
+ '.env',
18
+ '.env.local',
19
+ '.env.development',
20
+ '.env.development.local',
21
+ ];
22
+ /** One `PORT=3020` line. `export`, quotes and a trailing comment are allowed. */
23
+ const ENV_PORT_LINE = /^\s*(?:export\s+)?PORT\s*=\s*["']?(\d{1,5})["']?\s*(?:#.*)?$/gm;
24
+ /** The highest TCP port. */
25
+ const MAX_PORT = 65_535;
26
+ function toPort(digits) {
27
+ if (digits === undefined) {
28
+ return undefined;
29
+ }
30
+ const port = Number(digits);
31
+ return Number.isInteger(port) && port > 0 && port <= MAX_PORT ? port : undefined;
32
+ }
33
+ function readText(path) {
34
+ try {
35
+ return readFileSync(path, 'utf8');
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ }
41
+ /**
42
+ * The ports the project's `.env` files set with `PORT=`.
43
+ *
44
+ * Every file is read, not only the one a given loader would pick: the scan only probes candidates,
45
+ * so an extra port costs one connection attempt, while a missed one is the defect this fixes.
46
+ */
47
+ export function envFilePorts(directory, read = readText) {
48
+ const ports = new Set();
49
+ for (const file of ENV_PORT_FILES) {
50
+ const text = read(join(directory, file));
51
+ if (text === undefined) {
52
+ continue;
53
+ }
54
+ for (const match of text.matchAll(ENV_PORT_LINE)) {
55
+ const port = toPort(match[1]);
56
+ if (port !== undefined) {
57
+ ports.add(port);
58
+ }
59
+ }
60
+ }
61
+ return [...ports];
62
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Carry the network record of a document that unloaded into the session of the next document (#1944).
3
+ *
4
+ * A click on "Sign out" sends a POST and then loads `/sign-in`. The old document sends the POST's
5
+ * records before it unloads, so the daemon has them. But the new document connects as a new
6
+ * `Session` under the same id, and the act's verdict, every later `reticle_observe { action:
7
+ * "network" }` and every later `reticle_assert` read only that session. The POST went away with the
8
+ * old `Session` object, and a `net` check on a real request answered "no call matched".
9
+ *
10
+ * What is carried, and why only that:
11
+ *
12
+ * - Only the network records from the start of the agent's newest action on the old document. Those
13
+ * are the window that the act's verdict and the agent's next check read. Nothing is carried when
14
+ * no agent acted, so a person's own reloads do not move old traffic into a new page.
15
+ * - Not the departure record (`NetInitiator.NAVIGATION`). The new document records its own load.
16
+ * - Only when the new session's first stamped event is from a DIFFERENT document. The same document
17
+ * reconnecting after a bridge outage did not unload, and its requests can still be answered.
18
+ *
19
+ * How the loss is declared (AGENTS.md rule 11):
20
+ *
21
+ * - Every carried record has `fromDepartedDocument: true`. Its `t` is when it was carried, so it
22
+ * cannot show where the request was in the old document's timeline.
23
+ * - A request that failed with status 0 is not carried as a failure: the browser cancels a leaving
24
+ * document's requests, so that status is not the server's answer (see `abortedByUnload`).
25
+ * - A request that had no answer is marked `pageUnloaded: true`. Its answer went to a document that
26
+ * no longer exists. Its outcome is unknown: `net` checks that rest on it are `unknown`, never `no`,
27
+ * and settle does not wait for it (`neverAnswered`).
28
+ * - `documentId`, `seq`, `actionId` and `attribution` are removed. They are about the old document
29
+ * and its journal, and kept here they would make the old document the current one again.
30
+ */
31
+ import { type ReticleEvent } from '@reticlehq/core';
32
+ /** What the old document sent, and which document it was. */
33
+ export interface DepartedNetwork {
34
+ /** The old document's id, or undefined when its SDK does not stamp one. */
35
+ readonly documentId: string | undefined;
36
+ readonly events: readonly ReticleEvent[];
37
+ }
38
+ /** The part of a `Session` this needs. A real `Session` satisfies it. */
39
+ export interface DepartingSession {
40
+ readonly currentDocumentId: string | undefined;
41
+ readonly lastAct: {
42
+ cursor(): number | undefined;
43
+ };
44
+ /** The newest action id this session minted. Events in an open action window carry it. */
45
+ readonly lastActionId: string | undefined;
46
+ eventsSince(cursor: number): ReticleEvent[];
47
+ }
48
+ /** The part of a `Session` that receives the record. A real `Session` satisfies it. */
49
+ export interface ArrivingSession {
50
+ pushEvent(event: ReticleEvent): void;
51
+ onEvent(listener: (event: ReticleEvent) => void): () => void;
52
+ }
53
+ /**
54
+ * The network records the old document made after the agent's last act on it, ready to carry.
55
+ * Undefined when nothing is to be carried.
56
+ */
57
+ export declare function departedNetworkOf(session: DepartingSession): DepartedNetwork | undefined;
58
+ /**
59
+ * Give the new session the old document's record once it shows that it is a new document.
60
+ *
61
+ * Waits for the first event that carries a `documentId`. A different id means the old document
62
+ * unloaded: the record goes in, right after that event. The same id means the same document
63
+ * reconnected, and nothing goes in. An old document with no id cannot be told apart, so nothing
64
+ * goes in then either. Returns a function that stops the wait.
65
+ */
66
+ export declare function carryOnNewDocument(next: ArrivingSession, departed: DepartedNetwork): () => void;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Carry the network record of a document that unloaded into the session of the next document (#1944).
3
+ *
4
+ * A click on "Sign out" sends a POST and then loads `/sign-in`. The old document sends the POST's
5
+ * records before it unloads, so the daemon has them. But the new document connects as a new
6
+ * `Session` under the same id, and the act's verdict, every later `reticle_observe { action:
7
+ * "network" }` and every later `reticle_assert` read only that session. The POST went away with the
8
+ * old `Session` object, and a `net` check on a real request answered "no call matched".
9
+ *
10
+ * What is carried, and why only that:
11
+ *
12
+ * - Only the network records from the start of the agent's newest action on the old document. Those
13
+ * are the window that the act's verdict and the agent's next check read. Nothing is carried when
14
+ * no agent acted, so a person's own reloads do not move old traffic into a new page.
15
+ * - Not the departure record (`NetInitiator.NAVIGATION`). The new document records its own load.
16
+ * - Only when the new session's first stamped event is from a DIFFERENT document. The same document
17
+ * reconnecting after a bridge outage did not unload, and its requests can still be answered.
18
+ *
19
+ * How the loss is declared (AGENTS.md rule 11):
20
+ *
21
+ * - Every carried record has `fromDepartedDocument: true`. Its `t` is when it was carried, so it
22
+ * cannot show where the request was in the old document's timeline.
23
+ * - A request that failed with status 0 is not carried as a failure: the browser cancels a leaving
24
+ * document's requests, so that status is not the server's answer (see `abortedByUnload`).
25
+ * - A request that had no answer is marked `pageUnloaded: true`. Its answer went to a document that
26
+ * no longer exists. Its outcome is unknown: `net` checks that rest on it are `unknown`, never `no`,
27
+ * and settle does not wait for it (`neverAnswered`).
28
+ * - `documentId`, `seq`, `actionId` and `attribution` are removed. They are about the old document
29
+ * and its journal, and kept here they would make the old document the current one again.
30
+ */
31
+ import { DepartedNetField, EventType, NetInitiator, asString, } from '@reticlehq/core';
32
+ /** The records of one request. Streams are left out: an open stream would hold settle open. */
33
+ const CARRIED_TYPES = new Set([
34
+ EventType.NET_PENDING,
35
+ EventType.NET_REQUEST,
36
+ EventType.NET_RESPONSE_BODY,
37
+ ]);
38
+ /**
39
+ * A request that failed with no HTTP status (status 0) in a document that then unloaded.
40
+ *
41
+ * A browser cancels the requests of a document it leaves, and the page sees that as a network
42
+ * error ("Failed to fetch"). The server can have received the request and answered it: driven
43
+ * against a POST that took 2 s to answer, the server logged the POST while the page recorded
44
+ * status 0. So the status 0 is not the server's answer. The record is left out, and its pending
45
+ * half is carried as `pageUnloaded`: unknown, not failed.
46
+ */
47
+ function abortedByUnload(event) {
48
+ return event.type === EventType.NET_REQUEST && 0 === event.data['status'];
49
+ }
50
+ /**
51
+ * Where the newest action's records start in the departed session, or undefined when no agent acted.
52
+ *
53
+ * Two sources, and the later one wins. `lastAct.cursor()` is set when an act's reply arrives. A
54
+ * click that calls `location.assign()` can unload the page before that reply, so the cursor stays
55
+ * at an OLDER act (or unset), and the request is left behind (driven: it was). The events of an
56
+ * open action window carry its id, so the first event with the newest id marks where that action
57
+ * began to have effects. The later of the two is the newest action, so older traffic stays out.
58
+ */
59
+ function newestActionStart(session) {
60
+ const cursor = session.lastAct.cursor();
61
+ const id = session.lastActionId;
62
+ const firstOfNewest = id === undefined ? undefined : session.eventsSince(0).find((e) => e.actionId === id)?.t;
63
+ if (cursor === undefined) {
64
+ return firstOfNewest;
65
+ }
66
+ if (firstOfNewest === undefined) {
67
+ return cursor;
68
+ }
69
+ return Math.max(cursor, firstOfNewest);
70
+ }
71
+ /** One carried record: the loss is declared and the old document's coordinates are removed. */
72
+ function carried(event, unanswered) {
73
+ const id = asString(event.data['id']);
74
+ const lost = event.type === EventType.NET_PENDING && id !== undefined && unanswered.has(id)
75
+ ? { [DepartedNetField.PAGE_UNLOADED]: true }
76
+ : {};
77
+ return {
78
+ t: event.t,
79
+ type: event.type,
80
+ sessionId: event.sessionId,
81
+ data: { ...event.data, [DepartedNetField.FROM_DEPARTED_DOCUMENT]: true, ...lost },
82
+ };
83
+ }
84
+ /**
85
+ * The network records the old document made after the agent's last act on it, ready to carry.
86
+ * Undefined when nothing is to be carried.
87
+ */
88
+ export function departedNetworkOf(session) {
89
+ const floor = newestActionStart(session);
90
+ if (floor === undefined) {
91
+ return undefined;
92
+ }
93
+ const window = session
94
+ .eventsSince(floor)
95
+ .filter((e) => CARRIED_TYPES.has(e.type) &&
96
+ e.data['initiator'] !== NetInitiator.NAVIGATION &&
97
+ !abortedByUnload(e));
98
+ if (0 === window.length) {
99
+ return undefined;
100
+ }
101
+ const answered = new Set();
102
+ for (const e of window) {
103
+ const id = asString(e.data['id']);
104
+ if (e.type === EventType.NET_REQUEST && id !== undefined) {
105
+ answered.add(id);
106
+ }
107
+ }
108
+ const unanswered = new Set();
109
+ for (const e of window) {
110
+ const id = asString(e.data['id']);
111
+ if (e.type === EventType.NET_PENDING && id !== undefined && !answered.has(id)) {
112
+ unanswered.add(id);
113
+ }
114
+ }
115
+ return {
116
+ documentId: session.currentDocumentId,
117
+ events: window.map((e) => carried(e, unanswered)),
118
+ };
119
+ }
120
+ /**
121
+ * Give the new session the old document's record once it shows that it is a new document.
122
+ *
123
+ * Waits for the first event that carries a `documentId`. A different id means the old document
124
+ * unloaded: the record goes in, right after that event. The same id means the same document
125
+ * reconnected, and nothing goes in. An old document with no id cannot be told apart, so nothing
126
+ * goes in then either. Returns a function that stops the wait.
127
+ */
128
+ export function carryOnNewDocument(next, departed) {
129
+ const stop = next.onEvent((event) => {
130
+ if (event.documentId === undefined) {
131
+ return;
132
+ }
133
+ stop();
134
+ if (departed.documentId === undefined || event.documentId === departed.documentId) {
135
+ return;
136
+ }
137
+ for (const record of departed.events) {
138
+ next.pushEvent(record);
139
+ }
140
+ });
141
+ return stop;
142
+ }
@@ -29,6 +29,15 @@ export interface NoSessionFacts {
29
29
  * lock. Named in the message so the reader can tell the two apart.
30
30
  */
31
31
  slowListeners?: readonly number[];
32
+ /**
33
+ * Ports that processes in this project's folder listen on, and the scan did not hear a page on.
34
+ *
35
+ * Never a place to send the agent, so never in `listening`: agent tools run in this folder and
36
+ * listen too, and one serves a page. An agent-browser viewer in `listening` produced "just open
37
+ * http://localhost:<port>" while the app was not running (reticle#1543). What these ports prove
38
+ * is less: the answer cannot say that nothing is listening.
39
+ */
40
+ folderListeners?: readonly number[];
32
41
  /** The port this daemon is on — half of the mismatch the old message asked about. */
33
42
  port: number;
34
43
  /**