@reticlehq/server 3.2.0 → 3.4.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 (437) hide show
  1. package/LICENSE-ENTERPRISE +1 -1
  2. package/README.md +3 -3
  3. package/SKILL.md +23 -22
  4. package/dist/chromium-launch-options.d.ts +4 -3
  5. package/dist/chromium-launch-options.js +6 -2
  6. package/dist/command/cli/adhoc-suite.d.ts +41 -0
  7. package/dist/command/cli/adhoc-suite.js +159 -0
  8. package/dist/command/cli/adhoc-verdict.d.ts +58 -2
  9. package/dist/command/cli/adhoc-verdict.js +108 -13
  10. package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
  11. package/dist/command/cli/answers/gate-hook-message.js +6 -0
  12. package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
  13. package/dist/command/cli/auth/cloud-keystore.js +0 -3
  14. package/dist/command/cli/auth/cloud-session.d.ts +4 -0
  15. package/dist/command/cli/auth/cloud-session.js +2 -0
  16. package/dist/command/cli/cli-doctor.js +30 -8
  17. package/dist/command/cli/cli-flow-commands.d.ts +3 -6
  18. package/dist/command/cli/cli-flow-commands.js +67 -4
  19. package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
  20. package/dist/command/cli/cli-parse-grammar.js +7 -0
  21. package/dist/command/cli/cli-parse-verify.d.ts +2 -5
  22. package/dist/command/cli/cli-parse-verify.js +86 -13
  23. package/dist/command/cli/cli-parse.d.ts +14 -1
  24. package/dist/command/cli/cli-parse.js +54 -8
  25. package/dist/command/cli/cli-update-commands.js +8 -1
  26. package/dist/command/cli/cli-verify.d.ts +80 -0
  27. package/dist/command/cli/cli-verify.js +181 -26
  28. package/dist/command/cli/cloud-cli.js +5 -2
  29. package/dist/command/cli/cloud-kit.d.ts +0 -2
  30. package/dist/command/cli/cloud-kit.js +3 -5
  31. package/dist/command/cli/cloud-login.js +21 -3
  32. package/dist/command/cli/demo/demo-page.js +6 -3
  33. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
  34. package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
  35. package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
  36. package/dist/command/cli/launch/cli-launch.d.ts +9 -29
  37. package/dist/command/cli/launch/cli-launch.js +27 -49
  38. package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
  39. package/dist/command/cli/launch/sdk-version-match.js +206 -0
  40. package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
  41. package/dist/command/cli/ports/resolve/cli-port.d.ts +38 -0
  42. package/dist/command/cli/ports/resolve/cli-port.js +169 -15
  43. package/dist/command/cli/report-command.d.ts +16 -0
  44. package/dist/command/cli/report-command.js +97 -0
  45. package/dist/command/cli/setup-mcp-cli.js +13 -13
  46. package/dist/command/cli/tutorial.d.ts +2 -2
  47. package/dist/command/cli/tutorial.js +21 -7
  48. package/dist/command/cli.js +67 -10
  49. package/dist/command/daemon/binding/daemon-status-probe.d.ts +44 -2
  50. package/dist/command/daemon/binding/daemon-status-probe.js +79 -6
  51. package/dist/command/daemon/binding/port-presence.d.ts +1 -1
  52. package/dist/command/daemon/daemon-resolve.d.ts +9 -0
  53. package/dist/command/daemon/daemon-resolve.js +1 -1
  54. package/dist/command/daemon/idle-shutdown.d.ts +2 -1
  55. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
  56. package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
  57. package/dist/command/dev/stale-issue-guard.d.ts +3 -4
  58. package/dist/command/dev/stale-issue-guard.js +8 -8
  59. package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
  60. package/dist/command/setup/bringup/connect-lease.js +38 -0
  61. package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
  62. package/dist/command/setup/bringup/dev-server-files.js +131 -0
  63. package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
  64. package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
  65. package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
  66. package/dist/command/setup/bringup/ensure-daemon.js +135 -16
  67. package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
  68. package/dist/command/setup/bringup/static-page-server.js +151 -0
  69. package/dist/command/setup/desktop-shape.d.ts +4 -0
  70. package/dist/command/setup/desktop-shape.js +18 -2
  71. package/dist/command/setup/init/init-host.js +8 -0
  72. package/dist/command/setup/init/init-port.d.ts +15 -0
  73. package/dist/command/setup/init/init-port.js +44 -0
  74. package/dist/command/setup/init/init-runtime.d.ts +1 -1
  75. package/dist/command/setup/init/init-runtime.js +39 -2
  76. package/dist/command/setup/node-effects.d.ts +27 -14
  77. package/dist/command/setup/node-effects.js +80 -44
  78. package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
  79. package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
  80. package/dist/command/setup/probe/page-probe.d.ts +13 -2
  81. package/dist/command/setup/probe/page-probe.js +18 -3
  82. package/dist/command/setup/run-setup.d.ts +26 -1
  83. package/dist/command/setup/run-setup.js +96 -13
  84. package/dist/command/setup/session-pick.d.ts +13 -0
  85. package/dist/command/setup/session-pick.js +20 -0
  86. package/dist/command/setup/setup-command.d.ts +2 -14
  87. package/dist/command/setup/setup-command.js +105 -31
  88. package/dist/command/setup/setup-mcp.js +3 -3
  89. package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
  90. package/dist/command/setup/terminal/wait-progress.js +17 -0
  91. package/dist/features/crawl/crawl-empty.js +1 -1
  92. package/dist/features/crawl/crawl-tools.d.ts +6 -0
  93. package/dist/features/crawl/crawl-tools.js +57 -0
  94. package/dist/features/crawl/crawl.d.ts +2 -0
  95. package/dist/features/crawl/crawl.js +171 -156
  96. package/dist/features/exhaust/app-coverage.d.ts +33 -0
  97. package/dist/features/exhaust/app-coverage.js +31 -0
  98. package/dist/features/exhaust/code-coverage.d.ts +47 -0
  99. package/dist/features/exhaust/code-coverage.js +77 -0
  100. package/dist/features/exhaust/exhaust-run.d.ts +33 -0
  101. package/dist/features/exhaust/exhaust-run.js +82 -0
  102. package/dist/features/exhaust/explorer.d.ts +103 -0
  103. package/dist/features/exhaust/explorer.js +195 -0
  104. package/dist/features/exhaust/failure-branches.d.ts +33 -0
  105. package/dist/features/exhaust/failure-branches.js +92 -0
  106. package/dist/features/exhaust/ledger.d.ts +202 -0
  107. package/dist/features/exhaust/ledger.js +252 -0
  108. package/dist/features/exhaust/session-fold.d.ts +31 -0
  109. package/dist/features/exhaust/session-fold.js +79 -0
  110. package/dist/features/exhaust/session-port.d.ts +36 -0
  111. package/dist/features/exhaust/session-port.js +102 -0
  112. package/dist/features/harness/drive-report.d.ts +0 -9
  113. package/dist/features/harness/drive-report.js +18 -0
  114. package/dist/features/harness/driver.js +4 -7
  115. package/dist/features/harness/fill-values.d.ts +6 -0
  116. package/dist/features/harness/fill-values.js +3 -0
  117. package/dist/features/harness/jev-driver.js +36 -22
  118. package/dist/features/harness/openai-driver.js +4 -7
  119. package/dist/features/harness/platform-config.d.ts +10 -0
  120. package/dist/features/harness/platform-config.js +13 -6
  121. package/dist/index.d.ts +10 -1
  122. package/dist/index.js +78 -104
  123. package/dist/judgement/capsule/capsule-store.d.ts +31 -2
  124. package/dist/judgement/capsule/capsule-store.js +58 -6
  125. package/dist/judgement/capsule/capsule.js +5 -0
  126. package/dist/judgement/capsule/causal-summary.d.ts +13 -1
  127. package/dist/judgement/capsule/causal-summary.js +7 -0
  128. package/dist/judgement/domain/domain-model.js +40 -9
  129. package/dist/judgement/oracles/self-instrument.js +4 -2
  130. package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
  131. package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
  132. package/dist/judgement/runs/artifact/gap-report.js +46 -0
  133. package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
  134. package/dist/judgement/runs/artifact/gap-summary.js +85 -0
  135. package/dist/judgement/runs/artifact/render-report.js +4 -1
  136. package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
  137. package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
  138. package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
  139. package/dist/judgement/runs/artifact/to-artifact.js +1 -1
  140. package/dist/judgement/runs/context-tools.js +21 -3
  141. package/dist/judgement/runs/drive-run.js +7 -2
  142. package/dist/judgement/runs/verification-sync.d.ts +3 -1
  143. package/dist/judgement/runs/verification-sync.js +1 -1
  144. package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
  145. package/dist/language/flows/annotate-notes/annotate.js +66 -18
  146. package/dist/language/flows/change/affected.d.ts +9 -0
  147. package/dist/language/flows/change/affected.js +35 -0
  148. package/dist/language/flows/change/gate.d.ts +20 -0
  149. package/dist/language/flows/change/gate.js +12 -1
  150. package/dist/language/flows/flow-anchor.d.ts +12 -0
  151. package/dist/language/flows/flow-anchor.js +20 -0
  152. package/dist/language/flows/flow-author.d.ts +7 -0
  153. package/dist/language/flows/flow-author.js +19 -0
  154. package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
  155. package/dist/language/flows/flow-expect-grammar.js +92 -11
  156. package/dist/language/flows/flow-intent.js +1 -1
  157. package/dist/language/flows/flow-journey.d.ts +54 -0
  158. package/dist/language/flows/flow-journey.js +119 -0
  159. package/dist/language/flows/flow-learning.d.ts +3 -0
  160. package/dist/language/flows/flow-learning.js +39 -8
  161. package/dist/language/flows/flow-replay-run.d.ts +33 -12
  162. package/dist/language/flows/flow-replay-run.js +131 -52
  163. package/dist/language/flows/flow-replay-types.d.ts +9 -0
  164. package/dist/language/flows/flow-replay.d.ts +27 -12
  165. package/dist/language/flows/flow-replay.js +127 -111
  166. package/dist/language/flows/flow-result.d.ts +2 -0
  167. package/dist/language/flows/flow-result.js +25 -0
  168. package/dist/language/flows/flow-step-runners.d.ts +26 -10
  169. package/dist/language/flows/flow-step-runners.js +20 -4
  170. package/dist/language/flows/flow-success.d.ts +21 -6
  171. package/dist/language/flows/flow-success.js +50 -138
  172. package/dist/language/flows/flow-tools.js +17 -5
  173. package/dist/language/flows/flows.d.ts +18 -5
  174. package/dist/language/flows/flows.js +101 -16
  175. package/dist/language/flows/heal.d.ts +9 -1
  176. package/dist/language/flows/heal.js +24 -2
  177. package/dist/language/flows/learned-sources.d.ts +21 -0
  178. package/dist/language/flows/learned-sources.js +84 -0
  179. package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
  180. package/dist/language/flows/recording/recording-backtrack.js +1 -1
  181. package/dist/language/flows/recording/replay-halt.d.ts +2 -1
  182. package/dist/language/flows/recording/replay-halt.js +3 -1
  183. package/dist/language/flows/recording/tape/recordings.d.ts +31 -2
  184. package/dist/language/flows/recording/tape/recordings.js +55 -1
  185. package/dist/language/flows/replay.d.ts +19 -0
  186. package/dist/language/flows/replay.js +53 -13
  187. package/dist/language/flows/stores/annotation-store.d.ts +5 -5
  188. package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
  189. package/dist/language/flows/stores/flake-store.js +2 -3
  190. package/dist/language/flows/suite/coverage.d.ts +1 -1
  191. package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
  192. package/dist/language/flows/verify-change-tools.js +19 -2
  193. package/dist/launch-chromium.d.ts +80 -0
  194. package/dist/launch-chromium.js +218 -0
  195. package/dist/memory/cloud/cloud-config.d.ts +11 -2
  196. package/dist/memory/cloud/cloud-config.js +47 -7
  197. package/dist/memory/cloud/cloud-sync.d.ts +4 -3
  198. package/dist/memory/cloud/cloud-sync.js +7 -10
  199. package/dist/memory/cloud/harness-config.d.ts +2 -0
  200. package/dist/memory/cloud/harness-offer.js +5 -6
  201. package/dist/memory/cloud/harness-switch.js +5 -6
  202. package/dist/memory/cloud/sync-cycle.d.ts +78 -1
  203. package/dist/memory/cloud/sync-cycle.js +321 -53
  204. package/dist/memory/cloud/sync-daemon.d.ts +13 -0
  205. package/dist/memory/cloud/sync-daemon.js +45 -20
  206. package/dist/memory/cloud/sync-disk.js +27 -8
  207. package/dist/memory/impact/impact-store.js +6 -0
  208. package/dist/memory/intent/inline-intent.js +8 -3
  209. package/dist/memory/intent/intent-shard-store.d.ts +33 -47
  210. package/dist/memory/intent/intent-shard-store.js +252 -140
  211. package/dist/memory/intent/intent-shard.d.ts +0 -23
  212. package/dist/memory/intent/intent-shard.js +1 -11
  213. package/dist/memory/intent/intent-store.d.ts +13 -8
  214. package/dist/memory/intent/intent-store.js +46 -90
  215. package/dist/memory/intent/intent-tools.js +34 -23
  216. package/dist/memory/journal/ambient-store.js +2 -3
  217. package/dist/memory/journal/deviation-report.js +4 -1
  218. package/dist/memory/journal/deviation-service.js +10 -2
  219. package/dist/memory/journal/drive-flow.d.ts +19 -14
  220. package/dist/memory/journal/drive-flow.js +65 -48
  221. package/dist/memory/journal/envelope-store.js +2 -3
  222. package/dist/memory/journal/journal-recorder.d.ts +21 -0
  223. package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
  224. package/dist/memory/journal/on-disk/retain-policy.js +44 -0
  225. package/dist/memory/journal/on-disk/retention.d.ts +14 -10
  226. package/dist/memory/journal/on-disk/retention.js +13 -12
  227. package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
  228. package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
  229. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -11
  230. package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -45
  231. package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
  232. package/dist/memory/journal/on-disk/workspace-tiers.js +90 -0
  233. package/dist/memory/journal/route-template.d.ts +22 -0
  234. package/dist/memory/journal/route-template.js +44 -0
  235. package/dist/memory/journal/session-end.d.ts +13 -2
  236. package/dist/memory/journal/session-end.js +88 -18
  237. package/dist/memory/journal/session-journal.d.ts +8 -1
  238. package/dist/memory/journal/session-journal.js +10 -0
  239. package/dist/memory/project/baselines.d.ts +1 -1
  240. package/dist/memory/project/dir/reticle-dir.d.ts +3 -1
  241. package/dist/memory/project/dir/reticle-dir.js +1 -0
  242. package/dist/memory/project/fs/fs-port.js +44 -5
  243. package/dist/memory/project/fs/write-atomic.d.ts +26 -0
  244. package/dist/memory/project/fs/write-atomic.js +48 -0
  245. package/dist/memory/project/project-tools.js +3 -1
  246. package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
  247. package/dist/memory/recall/prior/connection-memory.js +8 -0
  248. package/dist/memory/recall/registered-projects.d.ts +2 -1
  249. package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
  250. package/dist/portal/bridge/auth-failure-reason.js +9 -4
  251. package/dist/portal/bridge/bridge.d.ts +8 -0
  252. package/dist/portal/bridge/bridge.js +43 -2
  253. package/dist/portal/input/js-coverage.d.ts +25 -0
  254. package/dist/portal/input/js-coverage.js +30 -0
  255. package/dist/portal/input/real-input.d.ts +11 -0
  256. package/dist/portal/input/real-input.js +31 -5
  257. package/dist/portal/pool/browser-pool.d.ts +10 -0
  258. package/dist/portal/pool/browser-pool.js +10 -0
  259. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  260. package/dist/portal/pool/playwright-launcher.js +21 -13
  261. package/dist/portal/pool/pool-contract.d.ts +2 -0
  262. package/dist/portal/pool/storage-seed.d.ts +2 -1
  263. package/dist/portal/pool/zero-install.d.ts +27 -0
  264. package/dist/portal/pool/zero-install.js +54 -0
  265. package/dist/portal/realm/web-realm.js +6 -0
  266. package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
  267. package/dist/portal/session/dev-server/dev-command.js +30 -12
  268. package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
  269. package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
  270. package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
  271. package/dist/portal/session/dev-server/route-status-probe.js +57 -0
  272. package/dist/portal/session/dev-server/served-document.d.ts +21 -0
  273. package/dist/portal/session/dev-server/served-document.js +57 -0
  274. package/dist/portal/session/facts/background-traffic.d.ts +9 -0
  275. package/dist/portal/session/facts/background-traffic.js +25 -0
  276. package/dist/portal/session/facts/observed-state.d.ts +11 -0
  277. package/dist/portal/session/facts/observed-state.js +34 -0
  278. package/dist/portal/session/live-control-tools.js +48 -3
  279. package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
  280. package/dist/portal/session/no-session-diagnosis.js +62 -5
  281. package/dist/portal/session/no-session-next-action.d.ts +13 -0
  282. package/dist/portal/session/no-session-next-action.js +48 -9
  283. package/dist/portal/session/no-session-watch.d.ts +18 -0
  284. package/dist/portal/session/no-session-watch.js +88 -2
  285. package/dist/portal/session/presence/attachment-history.d.ts +13 -2
  286. package/dist/portal/session/session-health.d.ts +61 -0
  287. package/dist/portal/session/session-health.js +43 -3
  288. package/dist/portal/session/session-info.d.ts +6 -0
  289. package/dist/portal/session/session-manager.d.ts +29 -6
  290. package/dist/portal/session/session-manager.js +42 -5
  291. package/dist/portal/session/session-reaper.d.ts +28 -0
  292. package/dist/portal/session/session-reaper.js +49 -1
  293. package/dist/portal/session/session-successor.d.ts +11 -0
  294. package/dist/portal/session/session-successor.js +14 -1
  295. package/dist/portal/session/session-verdict-facts.d.ts +28 -0
  296. package/dist/portal/session/session-verdict-facts.js +15 -0
  297. package/dist/portal/session/session.d.ts +10 -0
  298. package/dist/portal/session/session.js +34 -26
  299. package/dist/surface/http-server.d.ts +2 -1
  300. package/dist/surface/http-server.js +4 -2
  301. package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
  302. package/dist/surface/mcp/mcp-proxy.js +5 -5
  303. package/dist/surface/mcp/mcp.js +4 -2
  304. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
  305. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
  306. package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
  307. package/dist/surface/mcp/server-instructions.js +21 -8
  308. package/dist/surface/tools/act/after-match.d.ts +17 -0
  309. package/dist/surface/tools/act/after-match.js +30 -0
  310. package/dist/surface/tools/act/already-true.d.ts +40 -0
  311. package/dist/surface/tools/act/already-true.js +36 -0
  312. package/dist/surface/tools/act/durable.d.ts +69 -0
  313. package/dist/surface/tools/act/durable.js +139 -0
  314. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +19 -6
  315. package/dist/surface/tools/act/navigation/navigate-arrival.js +60 -10
  316. package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
  317. package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
  318. package/dist/surface/tools/act/proved-controls.d.ts +9 -0
  319. package/dist/surface/tools/act/proved-controls.js +11 -0
  320. package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
  321. package/dist/surface/tools/act/sequence-grade.js +32 -1
  322. package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
  323. package/dist/surface/tools/act/settle-in-flight.js +29 -6
  324. package/dist/surface/tools/act-capsule.d.ts +7 -0
  325. package/dist/surface/tools/act-capsule.js +68 -8
  326. package/dist/surface/tools/act-sequence-tool.js +25 -8
  327. package/dist/surface/tools/act-tools.js +51 -74
  328. package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
  329. package/dist/surface/tools/args/numeric-bounds.js +30 -3
  330. package/dist/surface/tools/assert/assert-grade.js +26 -3
  331. package/dist/surface/tools/assert/assert-verdict.js +32 -6
  332. package/dist/surface/tools/browser-tools.js +17 -4
  333. package/dist/surface/tools/coverage-tools.js +27 -1
  334. package/dist/surface/tools/dynamic-tools.js +6 -0
  335. package/dist/surface/tools/error-recovery.js +4 -4
  336. package/dist/surface/tools/feedback-invite.js +6 -4
  337. package/dist/surface/tools/feedback-tools.d.ts +2 -1
  338. package/dist/surface/tools/feedback-tools.js +1 -1
  339. package/dist/surface/tools/harness-explore.d.ts +3 -2
  340. package/dist/surface/tools/harness-explore.js +7 -6
  341. package/dist/surface/tools/invoke-tool.js +17 -4
  342. package/dist/surface/tools/lease-hint.d.ts +8 -0
  343. package/dist/surface/tools/lease-hint.js +10 -0
  344. package/dist/surface/tools/lease-tools.d.ts +16 -28
  345. package/dist/surface/tools/lease-tools.js +68 -11
  346. package/dist/surface/tools/lineage-tools.js +1 -1
  347. package/dist/surface/tools/live-call-text.js +4 -1
  348. package/dist/surface/tools/observe-tools.js +23 -7
  349. package/dist/surface/tools/real-input-attempt.d.ts +0 -5
  350. package/dist/surface/tools/real-input-attempt.js +1 -1
  351. package/dist/surface/tools/tool-kit.d.ts +5 -0
  352. package/dist/surface/tools/tool-kit.js +6 -1
  353. package/dist/surface/tools/tool-surface.js +17 -5
  354. package/dist/surface/tools/tools.js +68 -29
  355. package/dist/telemetry/argument-shape.d.ts +0 -2
  356. package/dist/telemetry/argument-shape.js +0 -4
  357. package/dist/telemetry/error-class.d.ts +2 -3
  358. package/dist/telemetry/error-class.js +2 -3
  359. package/dist/telemetry/feedback-local.d.ts +1 -0
  360. package/dist/telemetry/feedback-local.js +1 -3
  361. package/dist/telemetry/feedback.d.ts +1 -1
  362. package/dist/telemetry/harness-drive.d.ts +0 -2
  363. package/dist/telemetry/harness-drive.js +0 -4
  364. package/dist/telemetry/hud-metrics.d.ts +32 -0
  365. package/dist/telemetry/hud-metrics.js +95 -0
  366. package/dist/telemetry/session-metrics.d.ts +5 -0
  367. package/dist/telemetry/session-metrics.js +15 -1
  368. package/dist/wire-journal.d.ts +34 -0
  369. package/dist/wire-journal.js +104 -0
  370. package/dist/workspace-packages.d.ts +0 -2
  371. package/dist/workspace-packages.js +1 -9
  372. package/docs/README.md +5 -2
  373. package/docs/actions.mdx +3 -3
  374. package/docs/agent-cheatsheet.md +4 -4
  375. package/docs/architecture.md +6 -6
  376. package/docs/best-practices.mdx +1 -1
  377. package/docs/capabilities.mdx +32 -1
  378. package/docs/cli/cloud.mdx +21 -9
  379. package/docs/cli/doctor.mdx +4 -0
  380. package/docs/cli/feedback.mdx +2 -2
  381. package/docs/cli/gate.mdx +9 -4
  382. package/docs/cli/init.mdx +1 -0
  383. package/docs/cli/report.mdx +44 -0
  384. package/docs/cli/update.mdx +10 -0
  385. package/docs/cli.mdx +9 -0
  386. package/docs/desktop.mdx +14 -10
  387. package/docs/environment.mdx +87 -0
  388. package/docs/faq.mdx +18 -16
  389. package/docs/first-drive.md +5 -13
  390. package/docs/flows.md +32 -2
  391. package/docs/frameworks.mdx +140 -9
  392. package/docs/getting-started.md +3 -3
  393. package/docs/http-transport.md +6 -3
  394. package/docs/human-control.md +2 -2
  395. package/docs/index.mdx +16 -6
  396. package/docs/install-agentic.mdx +37 -1
  397. package/docs/install-manual.mdx +1 -1
  398. package/docs/integration-patterns.md +3 -3
  399. package/docs/multi-agent-testing.md +1 -1
  400. package/docs/packages/babel-plugin.mdx +1 -1
  401. package/docs/packages/browser.mdx +2 -2
  402. package/docs/packages/react.mdx +2 -2
  403. package/docs/packages/tauri.mdx +1 -1
  404. package/docs/packages/vite-plugin.mdx +2 -2
  405. package/docs/packages.mdx +9 -9
  406. package/docs/predicates.mdx +176 -7
  407. package/docs/quickstart.mdx +2 -2
  408. package/docs/recipes.mdx +2 -2
  409. package/docs/state-management.mdx +2 -2
  410. package/docs/system-map.md +1 -1
  411. package/docs/telemetry.md +1 -1
  412. package/docs/token-efficiency.md +3 -3
  413. package/docs/tools/act-and-wait.mdx +1 -0
  414. package/docs/tools/act-sequence.mdx +1 -1
  415. package/docs/tools/console.mdx +1 -1
  416. package/docs/tools/inspect.mdx +1 -1
  417. package/docs/tools/network.mdx +1 -1
  418. package/docs/tools/query.mdx +11 -11
  419. package/docs/tools/sessions.mdx +1 -1
  420. package/docs/tools/snapshot.mdx +1 -1
  421. package/docs/tools/state.mdx +1 -1
  422. package/docs/tools/wait-for.mdx +1 -1
  423. package/docs/usage.md +8 -7
  424. package/docs/use-cases.mdx +157 -0
  425. package/docs/vs-screenshots.mdx +3 -3
  426. package/docs/what-is-recorded.md +62 -0
  427. package/docs/what-is-reticle.mdx +17 -20
  428. package/docs/why-reticle.mdx +14 -21
  429. package/package.json +11 -10
  430. package/dist/judgement/capsule/minimize.d.ts +0 -12
  431. package/dist/judgement/capsule/minimize.js +0 -22
  432. package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
  433. package/dist/judgement/outcome/predicate-to-expect.js +0 -148
  434. package/dist/judgement/phenomena/phenomena.d.ts +0 -26
  435. package/dist/judgement/phenomena/phenomena.js +0 -101
  436. package/dist/language/flows/flow-report.d.ts +0 -22
  437. package/dist/language/flows/flow-report.js +0 -106
@@ -10,7 +10,7 @@
10
10
  export declare const PROXY_IDLE_EXIT_MS: number;
11
11
  /** The local diagnostic emitted before an abandoned proxy exits. */
12
12
  export declare const PROXY_IDLE_EXIT_EVENT = "reticle_mcp_proxy_idle_exit";
13
- export interface ProxyIdleExitOptions {
13
+ interface ProxyIdleExitOptions {
14
14
  /** Called once after the client link has stayed quiet for the whole grace. */
15
15
  onExit: (idleMs: number) => void;
16
16
  /** True while a request or queued message still needs an answer. */
@@ -43,3 +43,4 @@ export declare class ProxyIdleExit {
43
43
  * zero as the explicit opt-out. Invalid values retain the conservative one-day default.
44
44
  */
45
45
  export declare function resolveProxyIdleExitMs(raw: string | undefined): number;
46
+ export {};
@@ -31,11 +31,22 @@ import { surfaceVocabulary, listOf } from './surface-vocabulary.js';
31
31
  * measured reason. A briefing that names a tool the agent was not given does not merely confuse it;
32
32
  * it makes the agent stop using the product altogether.
33
33
  */
34
- const verdictDiscipline = (v) => `Reticle verifies a running web app from the inside: go (${v.navigate}), look (${listOf(v.look, v.find)}), act and prove in one hop (${v.actAndWait}), observe (${listOf(v.observe, v.state, v.network, v.console)}), assert (${v.assert}). Verify a user-facing change against the real app before you call it done, and never weaken a check to make it pass.
34
+ /*
35
+ * `reticle gate` is FOLDED INTO the done sentence rather than given a paragraph, and that is the
36
+ * budget rule working rather than a stylistic choice.
37
+ *
38
+ * The instruction block has a hard byte cap whose own note says a raise must be paid for with a
39
+ * measurement, and that if one cannot be shown the right answer is to cut an older sentence. There
40
+ * is no measurement for `gate`: it is built, it has anti-reward-hacking, and it appeared in no
41
+ * surface an agent reads, which is an argument and not a number. Cutting a measured sentence to
42
+ * make room for an unmeasured one would be strictly worse. So it costs about a hundred characters
43
+ * inside the sentence that already names the exact moment it applies to, and the cap did not move.
44
+ */
45
+ const verdictDiscipline = (v) => `Reticle verifies a running web app from the inside: go (${v.navigate}), look (${listOf(v.look, v.find)}), act and prove in one hop (${v.actAndWait}), observe (${listOf(v.observe, v.state, v.network, v.console)}), assert (${v.assert}). Verify a user-facing change against the real app before you call it done — \`npx @reticlehq/server gate --since <ref>\` fails unless a passing artifact covers every flow your edits affect — and never weaken a check to make it pass.
35
46
 
36
- Only ${v.actAndWait} and ${v.assert} produce a verdict. ${v.act.length > 0 ? `${v.act} and e` : 'E'}verything else moves or reads the app and proves nothing, so a drive that ends without one of those two has no result however many tools it used. Prefer ${v.actAndWait}({ ref, action, until }) — it names the expected consequence BEFORE the action, which is the difference between a check and a rationalisation. Only verified:"yes" is a pass — "unknown" means Reticle could not tell what happened, "no-fault" means nothing was declared to prove. Report either as not proved.
47
+ Only ${v.act.length > 0 ? `${v.actAndWait}, ${v.assert} and ${v.act}{steps}` : `${v.actAndWait} and ${v.assert}`} produce a verdict. Everything else moves or reads the app and proves nothing, so a drive that ends without one of those two has no result however many tools it used. Prefer ${v.actAndWait}({ ref, action, until }) — it names the expected consequence BEFORE the action, which is the difference between a check and a rationalisation. Only verified:"yes" is a pass — "unknown" means Reticle could not tell what happened, "no-fault" means nothing was declared to prove. Report either as not proved.
37
48
 
38
- A "yes" over a clean capture IS the answer — re-reading the page after one finds the same state. When every consequence you set out to check has one, stop.
49
+ Done is the journey's END proved, not its first step: drive each step your change touches and give the last an \`until\` naming the end state (\`durable: true\` if it must persist). A "yes" on it over a clean capture IS the answer — stop.
39
50
 
40
51
  DIAGNOSING a bug? Read the source first — Reticle proves what the app DOES, not why. Drive to CONFIRM a fix, not to find one.`;
41
52
  /**
@@ -73,13 +84,15 @@ const replayFirst = (v) => {
73
84
  *
74
85
  * Deliberately concrete: the command, what it does to the app, and the call that confirms it. A
75
86
  * general instruction to "set Reticle up" is the state these agents are already in — they have the
76
- * tools and no app, and nothing tells them that combination is incomplete. It also names the dev
77
- * server restart, because a plugin added to a config the running server already read is the single
78
- * most common way this step half-succeeds.
87
+ * tools and no app, and nothing tells them that combination is incomplete. `init` starts the dev
88
+ * server and opens the app itself, so the restart is named only for the case it cannot cover: a dev
89
+ * server that was already running read the config before `init` edited it, which is the single most
90
+ * common way this step half-succeeds. Telling every agent to restart sent them to kill the server
91
+ * `init` had just started.
79
92
  */
80
93
  const firstMoveFor = (v) => `FIRST: no app has ever connected to Reticle in this project, so nothing here can be verified yet. Having these tools is not the same as being set up — the tools reach a daemon, and the daemon has nothing to look at until the app itself is instrumented.
81
94
 
82
- Fix that before anything else: run \`npx @reticlehq/server init\` in the project, then RESTART the dev server (a plugin added to a config the running server already read is not in the bundle), then load the app in a browser. Confirm with ${v.sessions} — a session listed there is the proof, and until one appears no other tool can tell you anything about this app.`;
95
+ Fix that before anything else: run \`npx @reticlehq/server init\` in the project. It starts the dev server and opens the app itself; only if a dev server was ALREADY running without Reticle, restart it and reload the tab. Confirm with ${v.sessions} — a session listed there is the proof, and until one appears no other tool can tell you anything about this app.`;
83
96
  /**
84
97
  * When to reach for the tools that are advertised but never explained, and how to reach the two
85
98
  * that are explained but not advertised. Both halves of the same defect.
@@ -100,7 +113,7 @@ Fix that before anything else: run \`npx @reticlehq/server init\` in the project
100
113
  */
101
114
  const reachFor = (v) => {
102
115
  const lines = [
103
- `${v.observe} is the evidence channel: everything the page did since a cursor, in one read. Take it BEFORE you call a build broken — measured, stripping the observation tools TRIPLED false alarms, because an agent that stops observing reaches for the verdict without the evidence. ${v.settle} settles a page that is changing without you; ${v.inspect} maps an element to its source file:line, which turns a finding into an edit${v.yield.length > 0 ? `; ${v.yield} hands the tab back to the human` : ''}.`,
116
+ `${v.observe} is the evidence channel: everything the page did since a cursor, in one read. Take it BEFORE you call a build broken — measured, stripping the observation tools TRIPLED false alarms. ${v.settle} settles a page that is changing without you; ${v.inspect} maps an element to its source file:line, which turns a finding into an edit${v.yield.length > 0 ? `; ${v.yield} hands the tab back to the human` : ''}.`,
104
117
  ];
105
118
  // Said ONLY when both halves are advertised. A catalogue with no way to invoke what it lists sends
106
119
  // the agent at a tool it cannot call and then off the product entirely — see
@@ -0,0 +1,17 @@
1
+ /**
2
+ * What act_and_wait does between the moment its predicate first holds and the moment it grades.
3
+ *
4
+ * The predicate resolves the INSTANT it holds, which on an optimistically-navigating app is while
5
+ * the write is still in flight — so the verdict was taken over a window the app had not finished,
6
+ * and came back `unknown / unsettled` asking the CALLER to re-check. A login that genuinely
7
+ * succeeded returned at 492ms of an 8000ms budget with every corroborating channel agreeing. So the
8
+ * rest of the budget the caller already granted is spent here, in three steps, each deciding
9
+ * nothing on its own.
10
+ */
11
+ import type { Predicate } from '@reticlehq/core';
12
+ import { waitForInFlight } from './settle-in-flight.js';
13
+ import { revertedAfterMatch } from './durable.js';
14
+ type Waited = Parameters<typeof waitForInFlight>[0] & Parameters<typeof revertedAfterMatch>[0];
15
+ /** Returns the sentence to fail with when the consequence reverted, else undefined. */
16
+ export declare function finishAfterMatch(session: Waited, until: Predicate, since: number, baselines: Parameters<typeof revertedAfterMatch>[3], remaining: () => number): Promise<string | undefined>;
17
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * What act_and_wait does between the moment its predicate first holds and the moment it grades.
3
+ *
4
+ * The predicate resolves the INSTANT it holds, which on an optimistically-navigating app is while
5
+ * the write is still in flight — so the verdict was taken over a window the app had not finished,
6
+ * and came back `unknown / unsettled` asking the CALLER to re-check. A login that genuinely
7
+ * succeeded returned at 492ms of an 8000ms budget with every corroborating channel agreeing. So the
8
+ * rest of the budget the caller already granted is spent here, in three steps, each deciding
9
+ * nothing on its own.
10
+ */
11
+ import { waitForInFlight } from './settle-in-flight.js';
12
+ import { waitForReaction } from './react-grace.js';
13
+ import { revertedAfterMatch } from './durable.js';
14
+ /** Returns the sentence to fail with when the consequence reverted, else undefined. */
15
+ export async function finishAfterMatch(session, until, since, baselines, remaining) {
16
+ const sleep = { sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) };
17
+ // 1. Follow any open request to its response. A response that arrives as a FAILURE still
18
+ // contradicts — and now arrives INSIDE the window, where the detector can see it at all — while a
19
+ // request still open when the budget runs out reports unsettled, an honest limit rather than an
20
+ // early exit. Costs nothing on the common path, where no request is in flight.
21
+ await waitForInFlight(session, since, remaining(), sleep);
22
+ // 2. Then the app's REACTION to that response. Close the window between the response and the
23
+ // re-render and every channel agrees the app took a successful write and did nothing —
24
+ // `response-ignored`, a `verified:"no"` on a correct app, produced entirely by where we stopped
25
+ // looking. Paid only in the shape that would otherwise be accused, and short enough that a
26
+ // genuinely dropped response is still reported.
27
+ await waitForReaction(session, since, remaining(), sleep);
28
+ // 3. Then ask whether what held still holds — see revertedAfterMatch.
29
+ return revertedAfterMatch(session, until, since, baselines);
30
+ }
@@ -0,0 +1,40 @@
1
+ import type { Predicate } from '@reticlehq/engine/question/predicate/predicate-schema.js';
2
+ import type { PredicateSession } from '@reticlehq/engine/question/predicate/predicate-session.js';
3
+ import type { Baselines } from '@reticlehq/engine/question/predicate/predicate.js';
4
+ /** Everything the pre-action check learned, which is three facts about one reading. */
5
+ export interface AlreadyTrueReading {
6
+ /** The consequence held BEFORE the action, so the action proved nothing about it. */
7
+ alreadyTrue: boolean;
8
+ /** That pre-existing match was against something hidden — a stronger reason to doubt the claim. */
9
+ alreadyTrueHiddenMatch: boolean;
10
+ /**
11
+ * WHAT was already true, kept rather than discarded.
12
+ *
13
+ * `already_true` tells an agent its assertion held before it acted; the next question is always
14
+ * "true how?" and it was unanswerable, because the reading decided the bit and was then thrown
15
+ * away. Afterwards the action has run and the state may have moved, so the one moment that
16
+ * mattered is gone. Present only when the answer is yes: a verdict that was genuinely caused
17
+ * carries its own evidence and needs no baseline beside it.
18
+ */
19
+ alreadyTrueEvidence?: unknown;
20
+ /**
21
+ * What each comparing leaf was reading BEFORE the action — the predicate language's past tense.
22
+ *
23
+ * Taken here because this is already the one place that looks at the app before it is touched,
24
+ * and a second pre-action pass would be a second round trip for a reading we are standing next
25
+ * to. Empty unless the caller actually asked for a comparison, so nothing pays for it.
26
+ */
27
+ baselines: Baselines;
28
+ }
29
+ /**
30
+ * Ask whether the declared consequence was already true, once, before the action.
31
+ *
32
+ * Only for predicates that read live DOM state. Event-based ones are floored at this act's cursor
33
+ * and cannot be satisfied by the past, so they need no pre-check and pay nothing. One extra query,
34
+ * on the path where a green is otherwise unfalsifiable.
35
+ *
36
+ * Gathered here rather than as three locals at the call site because they are three readings OF ONE
37
+ * THING, and `act-tools.ts` sits on the 1000-line cohesion cap: the rule says split before adding,
38
+ * and this is the unit that wanted splitting.
39
+ */
40
+ export declare function readAlreadyTrue(session: PredicateSession, until: Predicate | undefined, since: number): Promise<AlreadyTrueReading>;
@@ -0,0 +1,36 @@
1
+ import { evaluatePredicate } from '@reticlehq/engine/question/predicate/predicate.js';
2
+ import { readsDomState } from '@reticlehq/engine/evidence/already-true.js';
3
+ import { alreadyTrueHiddenMatch as alreadyTrueHiddenMatchOf } from '@reticlehq/engine/evidence/already-true.js';
4
+ import { captureBaselines } from '@reticlehq/engine/evidence/baseline.js';
5
+ /**
6
+ * Ask whether the declared consequence was already true, once, before the action.
7
+ *
8
+ * Only for predicates that read live DOM state. Event-based ones are floored at this act's cursor
9
+ * and cannot be satisfied by the past, so they need no pre-check and pay nothing. One extra query,
10
+ * on the path where a green is otherwise unfalsifiable.
11
+ *
12
+ * Gathered here rather than as three locals at the call site because they are three readings OF ONE
13
+ * THING, and `act-tools.ts` sits on the 1000-line cohesion cap: the rule says split before adding,
14
+ * and this is the unit that wanted splitting.
15
+ */
16
+ export async function readAlreadyTrue(session, until, since) {
17
+ // Before the pre-check, because the pre-check EVALUATES the predicate and a relative leaf with no
18
+ // baseline yet would report `inconclusive` — reading the app twice to answer a question we had
19
+ // not taken the reading for.
20
+ const baselines = await captureBaselines(session, until);
21
+ const precheck = until !== undefined && readsDomState(until)
22
+ ? await evaluatePredicate(session, until, since, false, baselines)
23
+ : undefined;
24
+ const alreadyTrue = precheck?.pass ?? false;
25
+ if (!alreadyTrue || until === undefined) {
26
+ return { alreadyTrue: false, alreadyTrueHiddenMatch: false, baselines };
27
+ }
28
+ return {
29
+ alreadyTrue: true,
30
+ baselines,
31
+ // The pre-check evidence is already in hand, so asking whether the match was against something
32
+ // hidden costs nothing and lets the message name it (#889).
33
+ alreadyTrueHiddenMatch: alreadyTrueHiddenMatchOf(until, precheck?.evidence),
34
+ ...(precheck?.evidence === undefined ? {} : { alreadyTrueEvidence: precheck.evidence }),
35
+ };
36
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * `act_and_wait { durable: true }` — after a yes, reload and ask again.
3
+ *
4
+ * Every verdict is scoped to one action's window, so "saved" was proved by the toast that said so,
5
+ * and nothing ever asked whether the thing was still there once the page came back. That is the
6
+ * difference between a UI that advanced and a change that persisted, and it was reachable only by
7
+ * hand: navigate, then a separate assert that nothing tied to the action that caused it.
8
+ *
9
+ * Only the part of `until` that describes the page as it IS can be asked of a fresh document. A
10
+ * request or a signal happened once, in a window that died with the old document; a route is read
11
+ * from route events a reload does not replay. So those clauses are dropped, never re-asserted.
12
+ */
13
+ import { z } from 'zod';
14
+ import { type Predicate } from '@reticlehq/core';
15
+ import type { decideVerified } from '@reticlehq/engine/evidence/verified.js';
16
+ type VerifiedVerdict = ReturnType<typeof decideVerified>;
17
+ import { evaluatePredicate } from '@reticlehq/engine/question/predicate/predicate.js';
18
+ import type { Session } from '../../../portal/session/session.js';
19
+ export declare const durableArg: z.ZodOptional<z.ZodBoolean>;
20
+ export declare const durableOutput: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
21
+ /** The part of a predicate a fresh document can answer, or undefined when there is none. */
22
+ export declare function durablePart(p: Predicate): Predicate | undefined;
23
+ /**
24
+ * What the re-check found. `held` absent means no answer: the page never came back to be asked, or
25
+ * it came back and could not answer (`inconclusive`).
26
+ */
27
+ export interface Durability {
28
+ held?: boolean;
29
+ observed?: string;
30
+ skipped?: string;
31
+ inconclusive?: string;
32
+ }
33
+ /**
34
+ * A re-check's predicate result, as durability. Only a page that ANSWERED can say the consequence is
35
+ * gone: an unreadable one (its store not up yet) or one the tab left mid-read is no answer, and
36
+ * reporting either as "did not persist" is a confident no about something nobody saw.
37
+ */
38
+ export declare function durabilityOf(verdict: {
39
+ pass: boolean;
40
+ observed?: string | undefined;
41
+ inconclusive?: string | undefined;
42
+ observationLost?: boolean | undefined;
43
+ }): Durability;
44
+ /**
45
+ * Apply `durable` to a decided verdict. Pure over `recheck`, which the caller supplies — see
46
+ * reloadAndRecheck for the one that reloads a real page.
47
+ */
48
+ export declare function withDurability(asked: unknown, until: Predicate, decision: VerifiedVerdict, recheck: (part: Predicate) => Promise<Durability>): Promise<{
49
+ decision: VerifiedVerdict;
50
+ durable?: Durability;
51
+ }>;
52
+ /** Reload the page, wait for it to re-announce itself, and ask `part` of the fresh document. */
53
+ export declare function reloadAndRecheck(deps: {
54
+ sessions: {
55
+ get(id: string): Session | undefined;
56
+ };
57
+ now: () => number;
58
+ }, session: Session, part: Predicate, timeoutMs: number): Promise<Durability>;
59
+ /**
60
+ * Whether the page-state part of a consequence that held has since gone — an optimistic UI that
61
+ * rolled back. Returns the sentence to fail with, or undefined when it still holds.
62
+ *
63
+ * The wait resolves the instant a predicate holds, and the in-flight wait after it only follows a
64
+ * request to its response. A row that appeared and was removed a tick later, with no failing
65
+ * request to contradict it, was a yes. Only element/text/state clauses are re-read: a request or a
66
+ * signal does not un-happen, and re-reading `settled` on a page that polls would accuse it falsely.
67
+ */
68
+ export declare function revertedAfterMatch(session: Parameters<typeof evaluatePredicate>[0], until: Predicate, since: number, baselines: Parameters<typeof evaluatePredicate>[4]): Promise<string | undefined>;
69
+ export {};
@@ -0,0 +1,139 @@
1
+ /**
2
+ * `act_and_wait { durable: true }` — after a yes, reload and ask again.
3
+ *
4
+ * Every verdict is scoped to one action's window, so "saved" was proved by the toast that said so,
5
+ * and nothing ever asked whether the thing was still there once the page came back. That is the
6
+ * difference between a UI that advanced and a change that persisted, and it was reachable only by
7
+ * hand: navigate, then a separate assert that nothing tied to the action that caused it.
8
+ *
9
+ * Only the part of `until` that describes the page as it IS can be asked of a fresh document. A
10
+ * request or a signal happened once, in a window that died with the old document; a route is read
11
+ * from route events a reload does not replay. So those clauses are dropped, never re-asserted.
12
+ */
13
+ import { z } from 'zod';
14
+ import { PredicateKind, ReticleCommand, Verified, VerifiedReason, } from '@reticlehq/core';
15
+ import { evaluatePredicate, waitForPredicate, } from '@reticlehq/engine/question/predicate/predicate.js';
16
+ import { RELOAD_RECONNECT_TIMEOUT_MS, waitForReconnect, } from '../../../portal/session/session-reconnect.js';
17
+ export const durableArg = z
18
+ .boolean()
19
+ .optional()
20
+ .describe('After a yes, reload and require the element/text/state part of `until` to hold again.');
21
+ // Loose and undescribed on purpose: the output surface is re-sent every turn. `because` carries
22
+ // the answer; this is the detail — { held?, observed?, skipped? }.
23
+ export const durableOutput = z.record(z.unknown()).optional();
24
+ const RE_READABLE = new Set([
25
+ PredicateKind.ELEMENT,
26
+ PredicateKind.TEXT,
27
+ PredicateKind.STATE,
28
+ ]);
29
+ /** The part of a predicate a fresh document can answer, or undefined when there is none. */
30
+ export function durablePart(p) {
31
+ if (PredicateKind.ALL_OF === p.kind) {
32
+ const kept = p.predicates.map(durablePart).filter((c) => c !== undefined);
33
+ if (0 === kept.length)
34
+ return undefined;
35
+ return 1 === kept.length ? kept[0] : { kind: PredicateKind.ALL_OF, predicates: kept };
36
+ }
37
+ // Narrowing an OR to the branches that survive would change the claim, not re-check it.
38
+ if (PredicateKind.ANY_OF === p.kind)
39
+ return p.predicates.every((c) => durablePart(c) === c) ? p : undefined;
40
+ if (PredicateKind.NOT === p.kind)
41
+ return durablePart(p.predicate) === p.predicate ? p : undefined;
42
+ return RE_READABLE.has(p.kind) ? p : undefined;
43
+ }
44
+ /**
45
+ * A re-check's predicate result, as durability. Only a page that ANSWERED can say the consequence is
46
+ * gone: an unreadable one (its store not up yet) or one the tab left mid-read is no answer, and
47
+ * reporting either as "did not persist" is a confident no about something nobody saw.
48
+ */
49
+ export function durabilityOf(verdict) {
50
+ const observed = verdict.observed === undefined ? {} : { observed: verdict.observed };
51
+ if (true === verdict.pass)
52
+ return { held: true, ...observed };
53
+ if (verdict.inconclusive !== undefined)
54
+ return { inconclusive: verdict.inconclusive, ...observed };
55
+ if (true === verdict.observationLost)
56
+ return observed;
57
+ return { held: false, ...observed };
58
+ }
59
+ /**
60
+ * Apply `durable` to a decided verdict. Pure over `recheck`, which the caller supplies — see
61
+ * reloadAndRecheck for the one that reloads a real page.
62
+ */
63
+ export async function withDurability(asked, until, decision, recheck) {
64
+ if (true !== asked || Verified.YES !== decision.verified)
65
+ return { decision };
66
+ const part = durablePart(until);
67
+ if (part === undefined) {
68
+ const skipped = 'nothing declared describes the page itself — a request or a signal happens once and cannot be seen again after a reload; add an element, text or state clause';
69
+ return { decision, durable: { skipped } };
70
+ }
71
+ const durable = await recheck(part);
72
+ if (true === durable.held)
73
+ return { decision, durable };
74
+ if (durable.inconclusive !== undefined) {
75
+ return {
76
+ durable,
77
+ decision: {
78
+ verified: Verified.UNKNOWN,
79
+ verifiedReason: VerifiedReason.INCONCLUSIVE,
80
+ because: `the consequence held, but the reloaded page could not answer whether it still does (${durable.inconclusive})`,
81
+ },
82
+ };
83
+ }
84
+ if (durable.held === undefined) {
85
+ return {
86
+ durable,
87
+ decision: {
88
+ verified: Verified.UNKNOWN,
89
+ verifiedReason: VerifiedReason.OBSERVATION_LOST,
90
+ because: `the consequence held, but the page did not come back after the reload to be asked again${durable.observed === undefined ? '' : ` (${durable.observed})`}`,
91
+ },
92
+ };
93
+ }
94
+ return {
95
+ durable,
96
+ decision: {
97
+ verified: Verified.NO,
98
+ verifiedReason: VerifiedReason.ASSERTION_FAILED,
99
+ because: `the consequence held, then was gone after a reload — it did not persist${durable.observed === undefined ? '' : ` (observed ${durable.observed})`}`,
100
+ },
101
+ };
102
+ }
103
+ /** Reload the page, wait for it to re-announce itself, and ask `part` of the fresh document. */
104
+ export async function reloadAndRecheck(deps, session, part, timeoutMs) {
105
+ // Same floor navigate { reload } sets: nothing from the old document may answer for the new one.
106
+ session.lastAct.markNavigated(session.elapsed());
107
+ const sent = await session.command(ReticleCommand.REFRESH, { hard: false });
108
+ if (!sent.ok)
109
+ return { observed: sent.error ?? 'the reload was refused' };
110
+ const back = await waitForReconnect({
111
+ current: () => deps.sessions.get(session.id),
112
+ previous: session,
113
+ timeoutMs: Math.max(timeoutMs, RELOAD_RECONNECT_TIMEOUT_MS),
114
+ now: deps.now,
115
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
116
+ });
117
+ const fresh = back ? deps.sessions.get(session.id) : undefined;
118
+ if (fresh === undefined)
119
+ return {};
120
+ return durabilityOf(await waitForPredicate(fresh, part, timeoutMs, 0));
121
+ }
122
+ /**
123
+ * Whether the page-state part of a consequence that held has since gone — an optimistic UI that
124
+ * rolled back. Returns the sentence to fail with, or undefined when it still holds.
125
+ *
126
+ * The wait resolves the instant a predicate holds, and the in-flight wait after it only follows a
127
+ * request to its response. A row that appeared and was removed a tick later, with no failing
128
+ * request to contradict it, was a yes. Only element/text/state clauses are re-read: a request or a
129
+ * signal does not un-happen, and re-reading `settled` on a page that polls would accuse it falsely.
130
+ */
131
+ export async function revertedAfterMatch(session, until, since, baselines) {
132
+ const part = durablePart(until);
133
+ if (part === undefined)
134
+ return undefined;
135
+ const now = await evaluatePredicate(session, part, since, true, baselines);
136
+ if (true === now.pass || now.inconclusive !== undefined)
137
+ return undefined;
138
+ return `held, then reverted before the action finished${now.observed === undefined ? '' : ` — now ${now.observed}`}`;
139
+ }
@@ -17,6 +17,7 @@
17
17
  * would make the daemon wait. `assert` / `wait_for` / `act_and_wait` all spend the caller's
18
18
  * `timeout_ms` (resolve-within.ts); this is the same budget applied to the same wait.
19
19
  */
20
+ import type { Session } from '../../../../portal/session/session.js';
20
21
  import type { SessionManager } from '../../../../portal/session/session-manager.js';
21
22
  import type { NavigateArrival } from './navigate-result.js';
22
23
  /**
@@ -24,6 +25,12 @@ import type { NavigateArrival } from './navigate-result.js';
24
25
  * and the SDK to dial back; short enough to not hang. A caller who knows the app is slower says so.
25
26
  */
26
27
  export declare const ARRIVAL_TIMEOUT_MS = 5000;
28
+ /**
29
+ * The navigation target as an absolute URL, resolved against the tab being navigated — the same
30
+ * resolution the browser applies. An agent passes a path (`/orders?id=4`) as often as a full URL,
31
+ * and `samePage` cannot parse a path, so without this a relative navigation could never confirm.
32
+ */
33
+ export declare function absoluteTarget(target: string, base: string): string;
27
34
  /**
28
35
  * Which session an arrival may be attributed to.
29
36
  *
@@ -39,14 +46,20 @@ export declare const ARRIVAL_TIMEOUT_MS = 5000;
39
46
  */
40
47
  export interface ArrivalScope {
41
48
  /**
42
- * The session the navigation was dispatched to.
49
+ * The actual Session object that received the navigation command.
50
+ *
51
+ * A full-document navigation can destroy this session and reconnect the new document
52
+ * with the same id or a different id, so successor detection needs the original object.
53
+ */
54
+ navigatedSession: Session;
55
+ /**
56
+ * The URL the session was on before navigation started.
43
57
  *
44
- * Exempt from the exclusion below, because it is legitimately at the target both before and after
45
- * a navigation that keeps the document — a reload, or a same-page route change. Excluding it would
46
- * report `confirmed: false` for the navigations most likely to have worked.
58
+ * This lets us distinguish "the navigation is still in progress" from
59
+ * "the app redirected the newly arrived document somewhere else."
47
60
  */
48
- navigatedId: string;
49
- /** Sessions already on the target BEFORE dispatch. They cannot be evidence of this arrival. */
61
+ navigatedFrom: string;
62
+ /** Sessions already on the target before dispatch cannot prove this navigation arrived. */
50
63
  priorIds: ReadonlySet<string>;
51
64
  }
52
65
  /**
@@ -17,6 +17,7 @@
17
17
  * would make the daemon wait. `assert` / `wait_for` / `act_and_wait` all spend the caller's
18
18
  * `timeout_ms` (resolve-within.ts); this is the same budget applied to the same wait.
19
19
  */
20
+ import { awaitDocumentSuccessor } from '../../../../portal/session/session-successor.js';
20
21
  /**
21
22
  * The default when the caller gives no `timeout_ms`: long enough for a dev server to serve a page
22
23
  * and the SDK to dial back; short enough to not hang. A caller who knows the app is slower says so.
@@ -41,6 +42,19 @@ function samePage(a, b) {
41
42
  return false;
42
43
  }
43
44
  }
45
+ /**
46
+ * The navigation target as an absolute URL, resolved against the tab being navigated — the same
47
+ * resolution the browser applies. An agent passes a path (`/orders?id=4`) as often as a full URL,
48
+ * and `samePage` cannot parse a path, so without this a relative navigation could never confirm.
49
+ */
50
+ export function absoluteTarget(target, base) {
51
+ try {
52
+ return new URL(target, base).href;
53
+ }
54
+ catch {
55
+ return target;
56
+ }
57
+ }
44
58
  /**
45
59
  * The sessions already on `target` — the set `ArrivalScope.priorIds` is built from, sampled before
46
60
  * the navigation is dispatched.
@@ -56,19 +70,27 @@ export function idsAtTarget(sessions, target) {
56
70
  }
57
71
  return ids;
58
72
  }
59
- function findArrival(sessions, target, scope) {
73
+ function findArrival(sessions, target, scope, currentSession) {
60
74
  let arrived = null;
61
75
  for (const s of sessions.all()) {
62
- if (!samePage(s.url, target))
76
+ // The currently tracked document gets priority because it is the strongest
77
+ // evidence of where this navigation went.
78
+ if (s.id === currentSession.id) {
79
+ if (samePage(s.url, target)) {
80
+ return { sessionId: s.id };
81
+ }
82
+ // Intermediate routes are not necessarily redirects. Keep polling so
83
+ // the document has a chance to reach the requested target.
63
84
  continue;
64
- // The tab we drove is the answer whenever it is there, not a tie broken by scan order.
65
- if (s.id === scope.navigatedId)
66
- return { sessionId: s.id };
85
+ }
86
+ // A different session that was already at the target cannot prove this
87
+ // navigation arrived.
67
88
  if (scope.priorIds.has(s.id))
68
89
  continue;
69
- // Keep looking rather than returning: the driven session may still be ahead of us.
70
- if (null === arrived)
71
- arrived = { sessionId: s.id };
90
+ if (samePage(s.url, target)) {
91
+ if (null === arrived)
92
+ arrived = { sessionId: s.id };
93
+ }
72
94
  }
73
95
  return arrived;
74
96
  }
@@ -83,11 +105,39 @@ const REAL_CLOCK = {
83
105
  export async function awaitArrival(sessions, target, scope, timeoutMs = ARRIVAL_TIMEOUT_MS, clock = REAL_CLOCK) {
84
106
  const deadline = clock.now() + timeoutMs;
85
107
  for (;;) {
86
- const found = findArrival(sessions, target, scope);
108
+ // Keep checking the live session registry for the entire caller-provided
109
+ // budget. This is important because a document can disconnect and reconnect
110
+ // under another id, and the new session may be at the requested target,
111
+ // including on a different origin.
112
+ const found = findArrival(sessions, target, scope, scope.navigatedSession);
87
113
  if (found !== null)
88
114
  return found;
89
115
  if (clock.now() >= deadline)
90
- return null;
116
+ break;
91
117
  await clock.sleep(POLL_MS);
92
118
  }
119
+ // Only after the navigation wait has expired do we use successor detection.
120
+ // At this point it is no longer being used to decide whether the requested
121
+ // navigation arrived. It only helps explain where the replaced document
122
+ // landed when the target was never observed.
123
+ const current = sessions.get(scope.navigatedSession.id);
124
+ if (current !== undefined) {
125
+ if (!samePage(current.url, scope.navigatedFrom) && !samePage(current.url, target)) {
126
+ return {
127
+ sessionId: current.id,
128
+ landedOn: current.url,
129
+ };
130
+ }
131
+ return null;
132
+ }
133
+ const successor = await awaitDocumentSuccessor(sessions, scope.navigatedSession, 0, clock);
134
+ if (successor !== null &&
135
+ !samePage(successor.url, target) &&
136
+ !samePage(successor.url, scope.navigatedFrom)) {
137
+ return {
138
+ sessionId: successor.id,
139
+ landedOn: successor.url,
140
+ };
141
+ }
142
+ return null;
93
143
  }
@@ -1,18 +1,7 @@
1
- /**
2
- * The honest envelope for a navigation nobody can confirm.
3
- *
4
- * `window.location.assign(url)` returns before the page moves, and the SDK that would report on the
5
- * new document is destroyed by the navigation itself. So a successful NAVIGATE means the browser
6
- * accepted the instruction — not that anything arrived. Driven against a dead URL it returned
7
- * `{"ok":true}` while the session died, which is the worst combination available: the agent believes
8
- * it navigated, is looking at nothing, and has lost Reticle.
9
- *
10
- * `reticle_act` already draws this line — `dispatched` (sent) versus `settled` (a frame flushed).
11
- * This gives navigate the same honesty without changing what it does.
12
- */
13
1
  /** The session the SDK reconnected as after the page moved, when one was observed in the window. */
14
2
  export interface NavigateArrival {
15
3
  sessionId: string;
4
+ landedOn?: string;
16
5
  }
17
6
  export declare function navigateResult(result: {
18
7
  ok?: unknown;
@@ -1,3 +1,16 @@
1
+ /**
2
+ * The honest envelope for a navigation nobody can confirm.
3
+ *
4
+ * `window.location.assign(url)` returns before the page moves, and the SDK that would report on the
5
+ * new document is destroyed by the navigation itself. So a successful NAVIGATE means the browser
6
+ * accepted the instruction — not that anything arrived. Driven against a dead URL it returned
7
+ * `{"ok":true}` while the session died, which is the worst combination available: the agent believes
8
+ * it navigated, is looking at nothing, and has lost Reticle.
9
+ *
10
+ * `reticle_act` already draws this line — `dispatched` (sent) versus `settled` (a frame flushed).
11
+ * This gives navigate the same honesty without changing what it does.
12
+ */
13
+ import { NAVIGATION_REDIRECT_NOTE } from '@reticlehq/core';
1
14
  export function navigateResult(result,
2
15
  /**
3
16
  * Arrival, if the daemon saw the SDK come back. `null` keeps the original honest envelope.
@@ -26,12 +39,17 @@ waitedMs) {
26
39
  if (!ok)
27
40
  return base;
28
41
  if (arrival !== null) {
42
+ const redirected = 'string' === typeof arrival.landedOn;
29
43
  return {
30
44
  ...base,
31
- confirmed: true,
32
- // The SDK that reconnects after a navigation is a NEW session with a new id. Returning it here
33
- // is the difference between a confirmation and a confirmation the agent can act on.
45
+ confirmed: !redirected,
34
46
  sessionId: arrival.sessionId,
47
+ ...(redirected
48
+ ? {
49
+ landedOn: arrival.landedOn,
50
+ note: `${NAVIGATION_REDIRECT_NOTE} Landed on: ${arrival.landedOn}`,
51
+ }
52
+ : {}),
35
53
  };
36
54
  }
37
55
  return {
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Remember the control an act_and_wait PROVED, for the coverage ledger's `proved` level.
3
+ *
4
+ * Structural rather than `Session`, and optional, so a test stub that never proves anything need not
5
+ * implement it — the same reason `beginAction` is optional on the crawl session.
6
+ */
7
+ export declare function noteProved(session: {
8
+ recordProvedFrom?: (actPayload: unknown) => void;
9
+ }, verified: string, actPayload: unknown): void;