@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
@@ -13,7 +13,7 @@ import { aliasParam } from './args/alias-args.js';
13
13
  import { resolveSessionWithin } from '../../portal/session/timing/resolve-within.js';
14
14
  import { WALL_CLOCK } from '../../portal/session/timing/wall-clock.js';
15
15
  import { timeoutMsSchema } from './args/numeric-bounds.js';
16
- import { captureAct, captureVerdictedAct, pathOf, wroteSince } from '../../language/flows/replay.js';
16
+ import { captureAct, captureVerdictedAct, pageOf, wroteSince } from '../../language/flows/replay.js';
17
17
  import { durableArg, durableOutput, reloadAndRecheck, withDurability } from './act/durable.js';
18
18
  import { ActionType, ActionWarning, ReticleCommand, CaptureLoss, DEFAULT_ASSERT_TIMEOUT_MS, InputMode, Verified, VerifiedReason, PredicateKind, checkIntent, } from '@reticlehq/core';
19
19
  import { leanActResult, mutatedWithin } from './act/act-view.js';
@@ -34,19 +34,19 @@ import { declaresState } from '@reticlehq/engine/question/predicate/predicate-as
34
34
  import { isStateUnwatched } from '@reticlehq/engine/evidence/blind-spots.js';
35
35
  import { hiddenMatchNote } from '@reticlehq/engine/evidence/already-true.js';
36
36
  import { inFlightRequestLabels, repeatedRequestLabels } from './act/settle-in-flight.js';
37
- import { finishAfterMatch } from './act/after-match.js';
37
+ import { awaitShownMatch, evaluateAfterAct, finishAfterMatch } from './act/after-match.js';
38
38
  import { noteProved } from './act/proved-controls.js';
39
- import { decideVerified } from '@reticlehq/engine/evidence/verified.js';
39
+ import { decideVerified, withDeclinedDialog } from '@reticlehq/engine/evidence/verified.js';
40
40
  import { buildHonestyBlock, honestyForVerdict } from '@reticlehq/engine/evidence/honesty.js';
41
- import { declaredExpectations, declaresBodyIndependentChannel, } from '@reticlehq/engine/question/declared.js';
41
+ import { declaredExpectations, declaresBodyIndependentChannel, namedReactions, provedConsequences, } from '@reticlehq/engine/question/declared.js';
42
42
  import { describeWaitTarget, namedNetIsInFlight } from '@reticlehq/engine/evidence/unsettled.js';
43
43
  import { saveFailedAssertCapsule } from './act-capsule.js';
44
44
  import { blastRadius, buildDivergenceCapsule, wireCapsule } from '../../judgement/capsule/capsule.js';
45
45
  import { predicateToExpectedLinks } from '@reticlehq/engine/question/predicate/predicate-to-links.js';
46
- import { absenceBlindSpotNote, buildCoverageStatement, blindSpotsFromState, transportGapNote, Coverage, impeachesCapture, } from '@reticlehq/engine/evidence/blind-spots.js';
46
+ import { absenceBlindSpotNote, buildCoverageStatement, canvasPaintSpots, blindSpotsFromState, failingClauseReadsLostChannel, restsOnCompleteWindow, transportGapNote, Coverage, impeachesCapture, } from '@reticlehq/engine/evidence/blind-spots.js';
47
47
  import { acceptedWriteLabels } from '@reticlehq/engine/evidence/accepted-write.js';
48
48
  import { unreadWriteLabels, unreadWriteReason } from '@reticlehq/engine/evidence/unread-outcome.js';
49
- import { evaluatePredicate, waitForPredicate, provenExpectedLinks, PredicateSchema, } from '@reticlehq/engine/question/predicate/predicate.js';
49
+ import { waitForPredicate, provenExpectedLinks, PredicateSchema, } from '@reticlehq/engine/question/predicate/predicate.js';
50
50
  import { sessionVerdictFacts } from '../../portal/session/session-verdict-facts.js';
51
51
  import { currentOf, healthEnvelope, refuseIfThrottled } from '../../portal/session/session-health.js';
52
52
  import { pausedShortCircuit, pausedOutputShape, withControl, PAUSED_NO_VERDICT, } from '../../portal/session/control-envelope.js';
@@ -54,14 +54,15 @@ import { asNumber, asRecord, asString } from '@reticlehq/core';
54
54
  import { sourceOf } from './tools-helpers.js';
55
55
  import { confirmedByDeclaration } from './act/act-danger.js';
56
56
  import { dispatchAct, preflightAct } from './act/act-preflight.js';
57
- import { actTargetNote, checkNet, followLostObservation } from './act/act-observation.js';
57
+ import { actInconclusive, checkNet, followLostObservation, NAVIGATION_FOLLOW_GRACE_MS, } from './act/act-observation.js';
58
58
  import { intentArg, sessionIdShape } from './tool-kit.js';
59
+ import { actCommand } from './act-command.js';
60
+ import { tryRealInput } from './real-input-attempt.js';
59
61
  import { verdictIntentGap } from '@reticlehq/engine/evidence/verdict-intent-gap.js';
60
62
  import { asActionType, gradeOf } from './act/act-helpers.js';
61
63
  import { resolveActTarget } from './act/act-target.js';
64
+ import { hiddenTargetField } from './act/resolve-target.js';
62
65
  import { screenRequestFindings, withAdvisories } from './act/screen-request.js';
63
- import { tryRealInput, rewriteUploadArgs, HOVER_NEEDS_POINTER_MSG } from './real-input-attempt.js';
64
- import { sessionRoot } from '../../memory/project/session-root.js';
65
66
  import { gradeOfPredicate } from './assert/assert-grade.js';
66
67
  import { readAlreadyTrue } from './act/already-true.js';
67
68
  import { engineBlindSpotsField, noteSourceSeen } from '../../portal/session/recorded-gaps.js';
@@ -71,45 +72,11 @@ import { engineBlindSpotsField, noteSourceSeen } from '../../portal/session/reco
71
72
  */
72
73
  const ACTION_TYPE_VALUES = Object.values(ActionType);
73
74
  const actionTypeEnum = z.enum(ACTION_TYPE_VALUES);
74
- /**
75
- * Single dispatch point for every ACT and ACT_SEQUENCE command.
76
- *
77
- * reticle_act, act_and_wait and act_sequence rewrite uploads here, once. The path resolves against
78
- * the session's project, not the daemon's folder, so a worktree uploads its own fixture (#1769).
79
- *
80
- * captureAct is called AFTER this, so the flow-recording captures the pre-rewrite args (the path
81
- * the agent actually wrote) rather than the base64 blob — replay would otherwise send 750 KiB of
82
- * base64 to the browser as if it were an inline content call.
83
- */
84
- export async function actCommand(deps, session, actArgs, timeoutMs) {
85
- const rewritten = await rewriteUploadArgs({ fs: deps.fs, reticleRoot: sessionRoot(deps, session.id) }, 'string' === typeof actArgs['action'] ? actArgs['action'] : '', asRecord(actArgs['args']));
86
- // Sequence and act_and_wait dispatch through here without the ACT handler's tryRealInput.
87
- // A synthetic hover reports dispatched/settled while CSS :hover never applies — drive a real
88
- // pointer when one is available, otherwise refuse rather than lie. A press on a lease takes its
89
- // real keyboard, as reticle_act does: a synthetic Tab moves no focus.
90
- const hover = ActionType.HOVER === actArgs['action'];
91
- if (hover || (ActionType.PRESS === actArgs['action'] && deps.realInput === undefined)) {
92
- const ref = asString(actArgs['ref']);
93
- if (hover && ref === undefined)
94
- throw new Error(HOVER_NEEDS_POINTER_MSG);
95
- const action = hover ? ActionType.HOVER : ActionType.PRESS;
96
- const real = await tryRealInput(deps, session, ref ?? '', action, actArgs);
97
- if (hover && real.result === undefined)
98
- throw new Error(HOVER_NEEDS_POINTER_MSG);
99
- if (real.result !== undefined) {
100
- const result = { dispatched: true, settled: real.settled, ...asRecord(real.result) };
101
- return { kind: 'command_result', id: action, ok: true, result };
102
- }
103
- }
104
- const bridgeArgs = { ...actArgs, args: rewritten };
105
- // What the acted element shows (test id, source) is noted in `Session.command`, Harness acts too.
106
- return session.command(ReticleCommand.ACT, bridgeArgs, timeoutMs);
107
- }
108
75
  export const ACT_TOOLS = [
109
76
  {
110
77
  name: ReticleTool.ACT,
111
78
  example: { ref: 'e42', action: 'fill', args: { value: 'hello' } },
112
- description: 'Act WITHOUT checking the result — if the action is supposed to cause something, use reticle_act_and_wait { until } instead and get the verdict in the same call. This tool returns immediately with a `since` cursor; observe the reaction yourself with reticle_observe. Use it for actions with no observable consequence to assert (focus, hover, scrollIntoView, a fill you are about to submit). One action against a ref: click|dblclick|hover|focus|fill|type|clear|select|check|uncheck|submit|press|scrollIntoView. Carries effect:{dispatched,targetMatched,visible,enabled,focusMoved,valueChanged,domMutatedWithin,occluded,occludedBy,scrolledIntoView} to tell "action missed" from "app didn\'t react"; dispatched=landed, settled=a real frame flushed, and a settle timeout never fails the tool. Fields at their uninformative default are OMITTED so a clean action collapses to its consequence: an absent dispatched/targetMatched/visible/enabled means true, an absent occluded/scrolledIntoView/valueChanged/defaultPrevented means false, an absent focusMoved/occludedBy means null. occluded=true means the click point is covered by another element (a real user could not click it) — synthetic dispatch still delivered the event; scrolledIntoView=true means an off-viewport target was scrolled in first. effect.alreadyAtValue=true (check/uncheck only) means the box already read as the requested state, so NOTHING was dispatched and the app was never told — the DOM property is not evidence the application holds that value, so assert the app\'s own state (a signal, a request, a derived control), not the box. inputMode is "real" (native CDP, no synthetic effect block) or "synthetic"; clicks default to the occlusion-honest synthetic path even when CDP is configured — pass args.native:true to force a trusted native click (file pickers, clipboard). args.holdMs keeps the pointer DOWN for that long between mousedown and mouseup — the only way to drive a hold-to-confirm control (hold-to-delete, hold-to-record, long-press); effect.heldMs reports the hold actually achieved, which a throttled background tab can stretch. args.confirmDangerous is a permission gate and NOT a duration: it allows a destructive control, it does not hold one. inputModeReason explains any real→synthetic choice so it is never silent. On a leased tab a press uses the real keyboard: Tab moves focus, and a press with a ref focuses that element first, so Enter or Space presses it as a person would. Full model (real-input, throttled tabs, `reticle drive`): node_modules/@reticlehq/server/docs/usage.md §18.',
79
+ description: 'Act WITHOUT checking the result — if the action is supposed to cause something, use reticle_act_and_wait { until } instead and get the verdict in the same call. This tool returns immediately with a `since` cursor; observe the reaction yourself with reticle_observe. Use it for actions with no observable consequence to assert (focus, hover, scrollIntoView, a fill you are about to submit). One action against a ref: click|dblclick|hover|focus|fill|type|clear|select|check|uncheck|submit|press|scrollIntoView|insertText. Carries effect:{dispatched,targetMatched,visible,enabled,focusMoved,valueChanged,domMutatedWithin,occluded,occludedBy,scrolledIntoView} to tell "action missed" from "app didn\'t react"; dispatched=landed, settled=a real frame flushed, and a settle timeout never fails the tool. Fields at their uninformative default are OMITTED so a clean action collapses to its consequence: an absent dispatched/targetMatched/visible/enabled means true, an absent occluded/scrolledIntoView/valueChanged/defaultPrevented means false, an absent focusMoved/occludedBy means null. occluded=true means the click point is covered by another element (a real user could not click it) — synthetic dispatch still delivered the event; scrolledIntoView=true means an off-viewport target was scrolled in first. effect.alreadyAtValue=true (check/uncheck only) means the box already read as the requested state, so NOTHING was dispatched and the app was never told — the DOM property is not evidence the application holds that value, so assert the app\'s own state (a signal, a request, a derived control), not the box. inputMode is "real" (native CDP, no synthetic effect block) or "synthetic"; clicks default to the occlusion-honest synthetic path even when CDP is configured — pass args.native:true to force a trusted native click (file pickers, clipboard). args.holdMs keeps the pointer DOWN for that long between mousedown and mouseup — the only way to drive a hold-to-confirm control (hold-to-delete, hold-to-record, long-press); effect.heldMs reports the hold actually achieved, which a throttled background tab can stretch. args.confirmDangerous is a permission gate and NOT a duration: it allows a destructive control, it does not hold one. inputModeReason explains any real→synthetic choice so it is never silent. For insertText, defaultPrevented=true with valueChanged=true is the success path on an editor that owns its input: it cancelled beforeinput and applied the text, so it is NOT a refusal. insertText refuses with an error when the surface is not contenteditable — an EditContext editor (Monaco, CodeMirror 6) is refused too, because a scripted update fires no textupdate for its model — or when the native command exists and fails. On a leased tab a press uses the real keyboard: Tab moves focus, and a press with a ref focuses that element first, so Enter or Space presses it as a person would. Full model (real-input, throttled tabs, `reticle drive`): node_modules/@reticlehq/server/docs/usage.md §18.',
113
80
  inputSchema: {
114
81
  ref: z
115
82
  .string()
@@ -123,7 +90,7 @@ export const ACT_TOOLS = [
123
90
  args: z
124
91
  .record(z.string(), z.unknown())
125
92
  .optional()
126
- .describe('Action-specific arguments: { value } for fill/select (select also drives an ARIA combobox: it opens it and clicks the option by its label), { text } for type/press (the key NAME, e.g. Escape or Tab), { modifiers: ["Meta","Shift"] } for a press shortcut (Meta/Control/Shift/Alt — a Cmd+K) or a click/dblclick (Shift+click range select), { button: "right" | "middle" } for a click (right sends contextmenu), { toRef } for drag (the ref to drop ON — without it the drag lands nowhere), { native: true } to force a trusted native click, { holdMs: N } to keep the pointer or the key DOWN for N ms (hold-to-confirm controls; effect.heldMs reports what was achieved), { confirmDangerous: true } to allow a potentially destructive control — a permission gate, NOT a duration. For press: { keys: ["Control","k"] } holds several keys down TOGETHER (pressed in order, released in reverse) and { code } names the physical key — both stay on the in-page dispatcher, the only path that can express them. For upload: { path } is a path on disk (absolute or relative to the project root; the daemon reads the file and delivers real bytes to the file picker — this is the way to verify document-ingestion flows); or { name, content?, type? } to supply inline bytes directly.'),
93
+ .describe('Action-specific arguments: { value } for fill/select (select also drives an ARIA combobox: it opens it and clicks the option by its label), { text } for type/press/insertText (the key NAME, e.g. Escape or Tab, for press/type — or the text to insert for insertText), { modifiers: ["Meta","Shift"] } for a press shortcut (Meta/Control/Shift/Alt — a Cmd+K) or a click/dblclick (Shift+click range select), { button: "right" | "middle" } for a click (right sends contextmenu), { toRef } for drag (the ref to drop ON — without it the drag lands nowhere), { at: { x, y } } for click/dblclick/hover/tap/drag to act at a point of the element as fractions 0..1 of its box (a canvas has no child elements), { to: { x, y } } for drag to end at a point of the drop target (or of the source), { native: true } to force a trusted native click, { holdMs: N } to keep the pointer or the key DOWN for N ms (hold-to-confirm controls; effect.heldMs reports what was achieved), { confirmDangerous: true } to allow a potentially destructive control — a permission gate, NOT a duration. For press: { keys: ["Control","k"] } holds several keys down TOGETHER (pressed in order, released in reverse) and { code } names the physical key — both stay on the in-page dispatcher, the only path that can express them. For upload: { path } is a path on disk (absolute or relative to the project root; the daemon reads the file and delivers real bytes to the file picker — this is the way to verify document-ingestion flows); or { name, content?, type? } to supply inline bytes directly.'),
127
94
  refuseWhenThrottled: z
128
95
  .boolean()
129
96
  .optional()
@@ -160,8 +127,9 @@ export const ACT_TOOLS = [
160
127
  const session = deps.sessions.resolve(asString(args['sessionId']));
161
128
  // Live-control: refuse to drive the page while the human has paused us (before any work).
162
129
  const paused = pausedShortCircuit(session);
163
- if (paused !== undefined)
130
+ if (paused !== undefined) {
164
131
  return paused;
132
+ }
165
133
  refuseIfThrottled(session, args['refuseWhenThrottled']);
166
134
  // Where this step RUNS, read before the action — not after.
167
135
  //
@@ -169,12 +137,13 @@ export const ACT_TOOLS = [
169
137
  // ambient tape cuts journeys on this value, and stamping the destination put the sign-in click
170
138
  // at the head of the page it navigated TO: 4 of 11 auto-captured flows began with
171
139
  // `login-submit` on `/deployments`, which is the exact flow the segmentation exists to prevent.
172
- const routeBefore = pathOf(session.url);
140
+ const routeBefore = pageOf(session.url);
173
141
  // Resolve `target` to a ref BEFORE the action window opens, so the lookup is not attributed to
174
142
  // the act and cannot be mistaken for something the action caused.
175
143
  const targetRef = await resolveActTarget(session, args);
176
- if ('error' === targetRef.kind)
144
+ if ('error' === targetRef.kind) {
177
145
  throw new Error(targetRef.message);
146
+ }
178
147
  const since = session.elapsed();
179
148
  // The act cursor + effect are marked only once the action has actually DISPATCHED (below, on
180
149
  // each success path) — a refused act must leave nothing behind for the next observe to judge.
@@ -204,23 +173,23 @@ export const ACT_TOOLS = [
204
173
  settled: real.settled,
205
174
  settleReason: null,
206
175
  result: leanActResult(real.result),
176
+ ...hiddenTargetField(targetRef),
207
177
  ...healthEnvelope(session),
208
178
  });
209
179
  }
210
180
  // actCommand is the single interception point: it rewrites upload+path args to real bytes
211
181
  // before any ACT command crosses the bridge, covering this call site and all others.
212
- const result = await actCommand(deps, session, {
213
- ref: targetRef.ref,
214
- action: args['action'],
215
- args: args['args'] ?? {},
216
- });
217
- if (!result.ok)
182
+ const plain = { ref: targetRef.ref, action: args['action'], args: args['args'] ?? {} };
183
+ const result = await actCommand(deps, session, ReticleCommand.ACT, plain, undefined, true);
184
+ if (!result.ok) {
218
185
  throw new Error(result.error ?? 'act failed');
186
+ }
219
187
  captureAct(deps.recordings, args, result.result, routeBefore, session.id);
220
188
  // lift dispatch/settle status to the envelope (a settle timeout is NOT a failure).
221
189
  const r = asRecord(result.result);
222
- if ('boolean' === typeof r['settled'])
190
+ if ('boolean' === typeof r['settled']) {
223
191
  settledOutcome = r['settled'];
192
+ }
224
193
  // Keep what only this call measured, so the observe that judges this window can ask whether
225
194
  // anything happened INSIDE the target — the one fact that separates a dead control from a
226
195
  // page that was merely busy with something else.
@@ -234,12 +203,13 @@ export const ACT_TOOLS = [
234
203
  settled: r['settled'] ?? null,
235
204
  settleReason: r['settleReason'] ?? null,
236
205
  result: leanActResult(result.result),
206
+ ...hiddenTargetField(targetRef),
237
207
  ...(true === real.fellBack ? { warning: ActionWarning.REAL_INPUT_FELL_BACK } : {}),
238
208
  ...healthEnvelope(session),
239
209
  });
240
210
  }
241
211
  finally {
242
- deps.recordings.markEnded(pathOf(session.url), session.id);
212
+ deps.recordings.markEnded(pageOf(session.url), session.id);
243
213
  // Close the window on every exit (settle or throw), recording the action + settle outcome.
244
214
  session.finishAction(undefined, settledOutcome, true === settledOutcome ? session.elapsed() - since : undefined);
245
215
  }
@@ -271,7 +241,7 @@ export const ACT_TOOLS = [
271
241
  args: z
272
242
  .record(z.string(), z.unknown())
273
243
  .optional()
274
- .describe('Action-specific arguments: { value } for fill/select (select also drives an ARIA combobox: it opens it and clicks the option by its label), { text } for type/press (the key NAME, e.g. Escape or Tab), { modifiers: ["Meta","Shift"] } for a press shortcut or a click (Meta/Control/Shift/Alt), { button: "right" | "middle" } for a click, { toRef } for drag (the ref to drop ON — without it the drag lands nowhere), { confirmDangerous: true } for a potentially destructive control. For upload: { path } is a path on disk (absolute or relative to project root; daemon reads real bytes) or { name, content?, type? } for inline bytes.'),
244
+ .describe('Action-specific arguments: { value } for fill/select (select also drives an ARIA combobox: it opens it and clicks the option by its label), { text } for type/press/insertText (the key NAME, e.g. Escape or Tab, for press/type — or the text to insert for insertText), { modifiers: ["Meta","Shift"] } for a press shortcut or a click (Meta/Control/Shift/Alt), { button: "right" | "middle" } for a click, { toRef } for drag (the ref to drop ON — without it the drag lands nowhere), { at: { x, y } } for click/dblclick/hover/tap/drag to act at a point of the element as fractions 0..1 of its box (a canvas has no child elements), { to: { x, y } } for drag to end at a point of the drop target (or of the source), { confirmDangerous: true } for a potentially destructive control. For upload: { path } is a path on disk (absolute or relative to project root; daemon reads real bytes) or { name, content?, type? } for inline bytes.'),
275
245
  predicate: PredicateSchema.optional().describe('Alias for `until`.'),
276
246
  until: PredicateSchema.optional().describe('Predicate to wait for after the action completes (same shape as reticle_assert). OMIT to wait for the page to SETTLE — network + DOM idle — the deterministic default instead of a sleep. To assert a consequence AND settle, allOf them: { kind: "allOf", predicates: [<your predicate>, { kind: "settled" }] }.'),
277
247
  timeout_ms: timeoutMsSchema
@@ -287,6 +257,10 @@ export const ACT_TOOLS = [
287
257
  },
288
258
  outputSchema: {
289
259
  durable: durableOutput,
260
+ budgetExtendedMs: z
261
+ .number()
262
+ .optional()
263
+ .describe("The re-ask on the successor ran on the caller's grace: a budget extension ON PURPOSE, taken only when the departure evidence showed the page navigating. Absent for every verdict that needed no follow."),
290
264
  effect: z
291
265
  .unknown()
292
266
  .optional()
@@ -401,8 +375,9 @@ export const ACT_TOOLS = [
401
375
  // BEFORE the action. A body clause this session cannot answer would fail whatever the app
402
376
  // did, and on a drive that mutates state the action is not always repeatable. See #801(C).
403
377
  const bodyRefusal = bodyClauseRefusal(until, session);
404
- if (bodyRefusal !== undefined)
378
+ if (bodyRefusal !== undefined) {
405
379
  throw new Error(bodyRefusal);
380
+ }
406
381
  const timeout = asNumber(args['timeout_ms']) ?? DEFAULT_ASSERT_TIMEOUT_MS;
407
382
  // An intent declared here lands in the ledger BEFORE the verdict is drawn, which is what makes
408
383
  // the undeclared-change gap silent on THIS verdict rather than the next one: it reads the
@@ -415,8 +390,9 @@ export const ACT_TOOLS = [
415
390
  // Resolve `target` to a ref BEFORE the action window opens, so the lookup is not attributed to
416
391
  // the act and cannot be mistaken for something the action caused.
417
392
  const resolved = await resolveActTarget(session, args);
418
- if ('error' === resolved.kind)
393
+ if ('error' === resolved.kind) {
419
394
  throw new Error(resolved.message);
395
+ }
420
396
  let since = session.elapsed();
421
397
  const actedSince = since;
422
398
  // The cursor + effect are marked after the act dispatches (below) — a refused act leaves none.
@@ -431,10 +407,11 @@ export const ACT_TOOLS = [
431
407
  // Was the consequence ALREADY TRUE, and what did that reading say? See act/already-true.ts.
432
408
  const { alreadyTrue, alreadyTrueHiddenMatch, alreadyTrueEvidence, baselines } = await readAlreadyTrue(session, until, since);
433
409
  // Same as the ACT handler: the route this step RAN on, before the action can move the app.
434
- const routeBeforeWait = pathOf(session.url);
435
- // Recorded in `finally`, once the verdict is known — see captureVerdictedAct.
410
+ const routeBeforeWait = pageOf(session.url);
411
+ // Recorded in `finally`, once the verdict is known — see captureVerdictedAct. `held`: the
412
+ // action made the declared claim hold.
436
413
  let dispatched;
437
- let recordedVerdict;
414
+ let held = false;
438
415
  try {
439
416
  // actCommand is the single interception point for upload+path rewrite.
440
417
  //
@@ -444,14 +421,15 @@ export const ACT_TOOLS = [
444
421
  // returns null instead (see act-preflight.ts for why a write is never re-sent), and the
445
422
  // successor block below then asks whether the consequence held on the NEW document — a
446
423
  // green if it did, an honest observation_lost if it did not.
447
- const actResult = await dispatchAct(() => actCommand(deps, session, {
424
+ const actResult = await dispatchAct(() => actCommand(deps, session, ReticleCommand.ACT, {
448
425
  ref: resolved.ref,
449
426
  action: args['action'],
450
427
  args: confirmedByDeclaration(asRecord(args['args']), args['target'], untilNamed),
451
428
  }));
452
429
  if (actResult !== null) {
453
- if (!actResult.ok)
430
+ if (!actResult.ok) {
454
431
  throw new Error(actResult.error ?? 'act failed');
432
+ }
455
433
  dispatched = { result: actResult.result };
456
434
  // Dispatched — now this act owns the cursor and the effect. Marking its OWN measurement
457
435
  // also stops the spread below from inheriting an earlier reticle_act's action and
@@ -462,9 +440,7 @@ export const ACT_TOOLS = [
462
440
  const predicateStarted = session.elapsed();
463
441
  let verdict = null === actResult
464
442
  ? { pass: false, observationLost: true }
465
- : timeout > 0
466
- ? await waitForPredicate(session, until, timeout, since, baselines)
467
- : await evaluatePredicate(session, until, since, true, baselines);
443
+ : await evaluateAfterAct(session, until, timeout, since, baselines);
468
444
  // The SDK may have gone away mid-act — see act-observation.ts.
469
445
  const followed = await followLostObservation({
470
446
  sessions: deps.sessions,
@@ -472,22 +448,28 @@ export const ACT_TOOLS = [
472
448
  verdict,
473
449
  timeout,
474
450
  predicateStarted,
451
+ since,
452
+ failedFollowGraceMs: NAVIGATION_FOLLOW_GRACE_MS,
475
453
  reevaluate: (next, budget) => waitForPredicate(next, until, budget, 0, baselines),
476
454
  });
477
- if (followed.followed)
455
+ if (followed.followed) {
478
456
  since = 0;
457
+ }
479
458
  session = followed.session;
480
459
  verdict = followed.verdict;
481
460
  // After a match, let the app finish before the window closes — see act/after-match.ts.
482
461
  if (verdict.pass && timeout > 0) {
483
462
  const left = () => timeout - (session.elapsed() - predicateStarted);
484
463
  const reverted = await finishAfterMatch(session, until, since, baselines, left);
485
- if (reverted !== undefined)
464
+ if (reverted !== undefined) {
486
465
  verdict = { pass: false, failureReason: reverted, observed: reverted };
466
+ }
467
+ verdict = await awaitShownMatch(session, until, since, baselines, verdict, left);
487
468
  }
488
469
  const r = asRecord(actResult?.result);
489
- if ('boolean' === typeof r['settled'])
470
+ if ('boolean' === typeof r['settled']) {
490
471
  settledOutcome = r['settled'];
472
+ }
491
473
  // Where the acted element is written, captured at act time even if the action unmounted it.
492
474
  const actedSource = sourceOf(r['source']);
493
475
  // Remembered so a LATER assertion can name a file with no element to point at, and so the
@@ -505,6 +487,9 @@ export const ACT_TOOLS = [
505
487
  // Read before the capsule, which needs it: a window the buffer trimmed cannot state an
506
488
  // absolute about what did NOT happen. See CausalSummary.truncated.
507
489
  const bufferLost = session.lostSince(since);
490
+ const impeachForLoss = bufferLost &&
491
+ (restsOnCompleteWindow(until) ||
492
+ (false === verdict.pass && failingClauseReadsLostChannel(until, windowEvents)));
508
493
  const capsule = verdict.pass
509
494
  ? undefined
510
495
  : buildDivergenceCapsule(links, windowEvents, bufferLost);
@@ -526,11 +511,13 @@ export const ACT_TOOLS = [
526
511
  // stronger than this block. Grade from the strongest asserted consequence; integrity from evictions.
527
512
  // Coverage: cross-origin frames / other blind spots the SDK reported during this window mean the
528
513
  // verdict didn't see everything — say so, never imply full coverage.
529
- const spots = blindSpotsFromState(session.blindSpots(), session.runtime);
514
+ const prior = since > 0 ? (await session.queryEvents({ since: 0 })).filter((e) => e.t < since) : [];
515
+ const canvas = canvasPaintSpots(windowEvents, { actionSince: since, prior });
516
+ const spots = [...blindSpotsFromState(session.blindSpots(), session.runtime), ...canvas];
530
517
  // The window's own counts, so sampling before it is not described as this window's (#1414).
531
518
  const windowSpots = blindSpotsFromState(session.blindSpots(since), session.runtime);
532
519
  const coverage = buildCoverageStatement(spots, windowSpots);
533
- const absenceBlindSpot = absenceBlindSpotNote(until, spots);
520
+ const absenceBlindSpot = absenceBlindSpotNote(until, spots, windowEvents);
534
521
  // A green resting on hidden matches says the node exists, not that it shows (#1408).
535
522
  const hiddenMatch = hiddenMatchNote(until, verdict.evidence);
536
523
  // Nothing subscribed ⇒ the state channel is dark, and the summary must say so rather than
@@ -565,13 +552,7 @@ export const ACT_TOOLS = [
565
552
  const honesty = buildHonestyBlock({
566
553
  grade,
567
554
  attribution: 'window',
568
- // Did the buffer lose scarce evidence FROM THIS WINDOW — not "did it evict anything while
569
- // the action ran", which was the previous rule and which is true on essentially every live
570
- // page. Age eviction retires everything past 60s on every push, and the churn floor is
571
- // sacrificed on purpose; neither costs this verdict a single event it needed. In the
572
- // field this made `unclean_capture` the dominant cause of `unknown`, reported for
573
- // evictions that happened outside the window they impeached.
574
- truncated: bufferLost,
555
+ truncated: impeachForLoss,
575
556
  coveragePartial: Coverage.PARTIAL === coverage.coverage,
576
557
  ...(coverage.note === undefined ? {} : { coverageNote: coverage.note }),
577
558
  // Settlement no longer vetoes a declared consequence that held, so the fact is carried
@@ -580,10 +561,9 @@ export const ACT_TOOLS = [
580
561
  ...(0 === impeachingNotes.length ? {} : { blindSpots: impeachingNotes }),
581
562
  losses,
582
563
  });
583
- // A refuted expectation must not ride into a regression flow cut from the ambient tape; it is
584
- // kept as the capsule below instead. See RecordingStore.unassertLast.
585
- if (!verdict.pass && actResult !== null)
586
- deps.recordings.unassertLast();
564
+ // A refuted or already-true claim stays off the ambient tape (the capsule below keeps a
565
+ // refuted one). See captureVerdictedAct.
566
+ held = verdict.pass && !alreadyTrue;
587
567
  const capsuleSaved = await saveFailedAssertCapsule({
588
568
  deps,
589
569
  verdict,
@@ -608,8 +588,8 @@ export const ACT_TOOLS = [
608
588
  // flagship path, and nothing said so: the finding was simply never produced.
609
589
  //
610
590
  // Learning material only. Attribution is unchanged — findings still come exclusively from
611
- // `windowEvents`, exactly as on the assert path.
612
- const prior = since > 0 ? (await session.queryEvents({ since: 0 })).filter((e) => e.t < since) : [];
591
+ // `windowEvents`, exactly as on the assert path. It is read above, before coverage, because the
592
+ // canvas-paint spot (#1525) learns the last canvas digest from it too.
613
593
  //
614
594
  // `actionSince` is the window's own floor here, which is the tightest attribution there is —
615
595
  // and it is what lets `duplicate-request` claim "one user action was performed" and mean it.
@@ -624,9 +604,10 @@ export const ACT_TOOLS = [
624
604
  namedNetUrls: declared.netUrls,
625
605
  repeatableNetUrls: declared.repeatableNetUrls,
626
606
  // A consequence that was already true before the action proves nothing about it, so it is
627
- // not evidence the destination rendered either — `alreadyTrue` decides that, once.
628
- renderProved: verdict.pass && !alreadyTrue && declared.rendersContent,
607
+ // not evidence the destination rendered or the route moved — `alreadyTrue` decides that, once.
608
+ ...provedConsequences(declared, verdict.pass && !alreadyTrue),
629
609
  ...session.lastAct.effect(),
610
+ ...namedReactions(declared),
630
611
  currentDocumentId: session.currentDocumentId,
631
612
  currentEditEpoch: session.currentEditEpoch,
632
613
  appOrigin: session.url,
@@ -644,8 +625,8 @@ export const ACT_TOOLS = [
644
625
  });
645
626
  const outcomeUnread = unreadWriteLabels(windowEvents);
646
627
  const stillInFlight = inFlightRequestLabels(windowEvents, session.url, session.background);
647
- // A click on a disabled control cannot cause anything, nor one no person could make.
648
- const inconclusive = verdict.inconclusive ?? actTargetNote(verdict.pass, actResult?.result);
628
+ // A timed-out read says why; a disabled or unreachable target cannot cause anything.
629
+ const inconclusive = actInconclusive(verdict, actResult?.result, session);
649
630
  const { decision, durable } = await withDurability(args['durable'], until, decideVerified({
650
631
  pass: verdict.pass,
651
632
  // Threaded rather than looked up: decideVerified is pure and has no session.
@@ -685,13 +666,13 @@ export const ACT_TOOLS = [
685
666
  unsettled: {
686
667
  waitedFor: describeWaitTarget(until),
687
668
  stillInFlight,
688
- // The retry loop that leaves nothing outstanding — see repeatedRequestLabels.
689
669
  repeated: repeatedRequestLabels(windowEvents),
690
670
  },
691
671
  ...(namedNetIsInFlight(until, stillInFlight) ? { namedRequestInFlight: true } : {}),
672
+ ...(impeachForLoss && false === verdict.pass ? { bufferLostEvidence: true } : {}),
673
+ ...withDeclinedDialog(windowEvents),
692
674
  }), (part) => reloadAndRecheck(deps, session, part, timeout));
693
- recordedVerdict = String(decision.verified);
694
- noteProved(session, recordedVerdict, actResult?.result);
675
+ noteProved(session, String(decision.verified), actResult?.result);
695
676
  // Computed once: the verdict block reports it, and the instrumentation gaps are a second
696
677
  // reading of the same evidence rather than a new observation.
697
678
  const actionSummary = causalSummary(windowEvents, {
@@ -785,6 +766,7 @@ export const ACT_TOOLS = [
785
766
  // An unobserved act has no effect to report, and inventing an empty one would read as
786
767
  // "the page did nothing" — a claim about the app, from a call that never saw it.
787
768
  ...(null === actResult ? {} : { effect: leanActResult(actResult.result) }),
769
+ ...hiddenTargetField(resolved),
788
770
  verdict,
789
771
  // Promoted out of `effect` on red only. On green nobody needs it and it is noise; on red it
790
772
  // is the first thing the agent wants, and burying a file:line inside the effect block is
@@ -827,15 +809,21 @@ export const ACT_TOOLS = [
827
809
  // Without its summary: it is byte-identical to `summary` two fields up. See wireCapsule.
828
810
  ...(capsule === undefined ? {} : { capsule: wireCapsule(capsule) }),
829
811
  ...(0 === greenRadius.length ? {} : { blastRadius: greenRadius }),
812
+ // The re-ask on the successor ran on the caller's grace — a budget extension on purpose,
813
+ // declared so the agent can see the extra time it was given (#2009).
814
+ ...(followed.budgetExtendedMs === undefined
815
+ ? {}
816
+ : { budgetExtendedMs: followed.budgetExtendedMs }),
830
817
  since,
831
818
  ...(session.id === actedSessionId ? {} : { sessionId: session.id }),
832
819
  ...healthEnvelope(currentOf(deps.sessions, session)),
833
820
  });
834
821
  }
835
822
  finally {
836
- if (dispatched !== undefined)
837
- captureVerdictedAct(deps.recordings, args, dispatched.result, routeBeforeWait, recordedVerdict, session.id, wroteSince(acted, actedSince));
838
- deps.recordings.markEnded(pathOf(currentOf(deps.sessions, session).url), session.id);
823
+ if (dispatched !== undefined) {
824
+ captureVerdictedAct(deps.recordings, args, dispatched.result, routeBeforeWait, held, session.id, wroteSince(acted, actedSince));
825
+ }
826
+ deps.recordings.markEnded(pageOf(currentOf(deps.sessions, session).url), session.id);
839
827
  acted.finishAction(verdictEffect, settledOutcome, true === settledOutcome ? acted.elapsed() - actedSince : undefined);
840
828
  }
841
829
  },
@@ -1,4 +1,4 @@
1
- export declare const STALE_BUILD_NOTICE = "Reticle's code on disk changed after this daemon started, so it is still running the old build. Restart it (`npx @reticlehq/server restart`) and retry before trusting a fix to Reticle itself.";
1
+ export declare const STALE_BUILD_NOTICE = "Reticle's code on disk changed after this daemon started, so it is still running the old build. Restart it (`npx @reticlehq/server serve --restart`) and retry before trusting a fix to Reticle itself.";
2
2
  /** Newest `.js` mtime under `dir`, or 0 when it cannot be read. */
3
3
  export declare function newestJsMtime(dir: string): number;
4
4
  /** A watcher over one directory, started now. Exported for tests; the daemon uses the default. */
@@ -14,7 +14,7 @@ import { dirname, join, resolve } from 'node:path';
14
14
  import { fileURLToPath } from 'node:url';
15
15
  /** How often the walk may run. A few hundred stats, so cheap, but not on every call. */
16
16
  const CHECK_EVERY_MS = 30_000;
17
- export const STALE_BUILD_NOTICE = "Reticle's code on disk changed after this daemon started, so it is still running the old build. Restart it (`npx @reticlehq/server restart`) and retry before trusting a fix to Reticle itself.";
17
+ export const STALE_BUILD_NOTICE = "Reticle's code on disk changed after this daemon started, so it is still running the old build. Restart it (`npx @reticlehq/server serve --restart`) and retry before trusting a fix to Reticle itself.";
18
18
  /** `dist/` of this package: this file sits at dist/surface/tools/agent/. */
19
19
  const DIST_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
20
20
  /** Newest `.js` mtime under `dir`, or 0 when it cannot be read. */
@@ -8,18 +8,19 @@ import { hiddenMatchNote } from '@reticlehq/engine/evidence/already-true.js';
8
8
  import { provenExpectedLinks, } from '@reticlehq/engine/question/predicate/predicate.js';
9
9
  import { findContradictions, } from '@reticlehq/engine/disagreement/contradictions.js';
10
10
  import { crashedRuleNotes } from '@reticlehq/engine/disagreement/contradiction-folds.js';
11
- import { declaredExpectations, declaresBodyIndependentChannel, } from '@reticlehq/engine/question/declared.js';
12
- import { absenceBlindSpotNote, blindSpotsFromState, buildCoverageStatement, restsOnCompleteWindow, Coverage, impeachesCapture, transportGapNote, } from '@reticlehq/engine/evidence/blind-spots.js';
11
+ import { declaredExpectations, declaresBodyIndependentChannel, provedConsequences, } from '@reticlehq/engine/question/declared.js';
12
+ import { absenceBlindSpotNote, blindSpotsFromState, buildCoverageStatement, failingClauseReadsLostChannel, restsOnCompleteWindow, Coverage, impeachesCapture, transportGapNote, } from '@reticlehq/engine/evidence/blind-spots.js';
13
13
  import { buildHonestyBlock } from '@reticlehq/engine/evidence/honesty.js';
14
14
  import { acceptedWriteLabels, hasAcceptedWrite, } from '@reticlehq/engine/evidence/accepted-write.js';
15
15
  import { unreadWriteLabels, unreadWriteReason } from '@reticlehq/engine/evidence/unread-outcome.js';
16
- import { decideVerified } from '@reticlehq/engine/evidence/verified.js';
16
+ import { decideVerified, withDeclinedDialog } from '@reticlehq/engine/evidence/verified.js';
17
17
  import { describeWaitTarget, namedNetIsInFlight } from '@reticlehq/engine/evidence/unsettled.js';
18
18
  import { inFlightRequestLabels, repeatedRequestLabels, } from '../act/settle-in-flight.js';
19
19
  import { gradeOfPredicate } from './assert-grade.js';
20
20
  import { assertSource, stampedSourceIn } from './assert-source.js';
21
21
  import { VerdictAttribution, verdictAttributionOf } from '@reticlehq/core';
22
22
  import { noteSourceSeen } from '../../../portal/session/recorded-gaps.js';
23
+ import { diagnoseTimeout } from '../act/act-observation.js';
23
24
  /**
24
25
  * The honesty verdict for a plain `reticle_assert`.
25
26
  *
@@ -62,7 +63,6 @@ changeUndeclared) {
62
63
  // The window's own counts, so sampling before it is not described as this window's (#1414).
63
64
  const windowSpots = blindSpotsFromState(session.blindSpots(since), session.runtime);
64
65
  const statement = buildCoverageStatement(spots, windowSpots);
65
- const absenceBlindSpot = absenceBlindSpotNote(predicate, spots);
66
66
  // A green resting on hidden matches says the node exists, not that it shows (#1408).
67
67
  const hiddenMatch = hiddenMatchNote(predicate, evidence);
68
68
  // Omitted entirely when coverage is full, so an intact page pays nothing and the field's PRESENCE
@@ -74,6 +74,8 @@ changeUndeclared) {
74
74
  }
75
75
  : {};
76
76
  const windowEvents = await session.queryEvents({ since });
77
+ // The window lets a target the page was seen to remove escape the virtualized blind spot.
78
+ const absenceBlindSpot = absenceBlindSpotNote(predicate, spots, windowEvents);
77
79
  // Everything before the window, passed as LEARNING material only. A scale error disagrees with a
78
80
  // value the API stated earlier in the session, which an action-scoped window can never contain —
79
81
  // measured live, `observe` over a wide window reported `unit-mismatch` while `assert` on the same
@@ -98,7 +100,7 @@ changeUndeclared) {
98
100
  expectedFailures: declared.netFailures,
99
101
  namedNetUrls: declared.netUrls,
100
102
  repeatableNetUrls: declared.repeatableNetUrls,
101
- renderProved: pass && declared.rendersContent,
103
+ ...provedConsequences(declared, pass),
102
104
  ...(actCursor !== undefined && actCursor >= since ? { actionSince: actCursor } : {}),
103
105
  });
104
106
  // Only a spot that IMPEACHES the capture downgrades a general verdict. Structural boundaries are
@@ -138,7 +140,9 @@ changeUndeclared) {
138
140
  });
139
141
  const outcomeUnread = unreadWriteLabels(windowEvents);
140
142
  const stillInFlight = inFlightRequestLabels(windowEvents, session.url, session.background);
141
- const effectiveInconclusive = inconclusive ?? (!pass ? session.preconditionFailure?.() : undefined);
143
+ const rawInconclusive = inconclusive ?? (!pass ? session.preconditionFailure?.() : undefined);
144
+ // A timed-out page read is the one `unknown` whose cause and fix are knowable here (#1990).
145
+ const effectiveInconclusive = diagnoseTimeout(rawInconclusive, session);
142
146
  /**
143
147
  * Does a green here rest on the window having been COMPLETE?
144
148
  *
@@ -153,13 +157,13 @@ changeUndeclared) {
153
157
  /**
154
158
  * The buffer evicted scarce evidence from this window.
155
159
  *
156
- * assert never consulted the buffer at all, on the reasoning that it "observes an already-open
157
- * window" — but eviction happens on push regardless of who opened the window, so
158
- * `{ console, absent: true }` returned `yes` over a window whose evidence was gone, which is
159
- * absence of evidence read as evidence of absence. The act path needs no `restsOnComplete` guard:
160
- * its cursor is the action's own.
160
+ * Two cases. (1) An ABSENCE claim needs a complete window; things dropped are the disproof it
161
+ * cannot see. (2) A FAILING positive `allOf` whose NET/SIGNAL/LOG child has no evidence left in
162
+ * the window — the child might have failed because the buffer dropped the request, not because
163
+ * the app never made it (#1231). Only the second reads the events; the first is structural.
161
164
  */
162
- const bufferLost = session.lostSince(since) && restsOnComplete;
165
+ const bufferLost = session.lostSince(since) &&
166
+ (restsOnComplete || (false === pass && failingClauseReadsLostChannel(predicate, windowEvents)));
163
167
  /** The durable ledger refused writes, on a query path that read from it. Same rule, other store. */
164
168
  const ledgerLost = ledgerClosed && restsOnComplete;
165
169
  const decision = decideVerified({
@@ -182,6 +186,8 @@ changeUndeclared) {
182
186
  ...(absenceBlindSpot === undefined ? {} : { absenceBlindSpot }),
183
187
  ...(hiddenMatch === undefined ? {} : { hiddenMatch }),
184
188
  ...(namedNetIsInFlight(predicate, stillInFlight) ? { namedRequestInFlight: true } : {}),
189
+ ...(bufferLost && false === pass ? { bufferLostEvidence: true } : {}),
190
+ ...withDeclinedDialog(windowEvents),
185
191
  honesty: buildHonestyBlock({
186
192
  grade: gradeOfPredicate(predicate),
187
193
  attribution: 'window',