@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
@@ -7,7 +7,7 @@ icon: filter
7
7
 
8
8
  A predicate declares what should be true, as `{ kind, ...fields }`. Reticle has twelve kinds: `signal`, `state`, `net`, `route`, `element`, `text`, `console`, `animation`, `settled`, and the combinators `allOf`, `anyOf` and `not`. Which one you pick decides whether a green means anything.
9
9
 
10
- It is the argument to `until` on [`reticle_act_and_wait`](/tools/act-and-wait), and to `predicate` on [`reticle_assert`](/tools/assert) and [`reticle_wait_for`](/tools/wait-for).
10
+ It is the argument to `until` on [`reticle_act_and_wait`](/tools/act-and-wait), and to `predicate` on [`reticle_assert`](/tools/assert) and [`reticle_assert { action: "wait" }`](/tools/wait-for).
11
11
 
12
12
  Every predicate is `{ kind, ...fields }`.
13
13
 
@@ -35,6 +35,7 @@ Take the advice. `element` and `text` are convenient and weak; `signal`, `state`
35
35
  | `element` / `text` | weak | Something rendered. A mock renders too |
36
36
  | `animation` | weak | An animation ran or finished |
37
37
  | `settled` | timing | The page went quiet. A precondition, not a proof |
38
+ | `compare` | its sides | Two readings agree: the stronger side's grade |
38
39
 
39
40
  ## The leaves
40
41
 
@@ -48,7 +49,17 @@ Take the advice. `element` and `text` are convenient and weak; `signal`, `state`
48
49
  }
49
50
  ```
50
51
 
51
- `query` accepts `role`, `name`, `text`, `label`, `placeholder`, `testid`, `alt`, `scope`. `state` is one of `visible`, `hidden`, `enabled`, `disabled`, `checked`, `expanded`, `focused`, `present`.
52
+ `query` accepts `role`, `name`, `text`, `label`, `placeholder`, `testid`, `alt`, `value`, `by`, `component`, `source`, `scope`, `self`, `attrs`. `state` is one of `visible`, `hidden`, `enabled`, `disabled`, `checked`, `expanded`, `pressed`, `focused`, `inViewport`, `present`.
53
+
54
+ `value` is the one worth knowing about. `{ "role": "textbox", "name": "Amount" }` collapses to "a textbox called Amount exists", which passes against an empty field; adding `value` is what makes it an assertion about what the user will actually send. `by` selects the locator strategy explicitly, `component` and `source` match React component identity and source location, and `self` restricts a scoped match to the scope element itself rather than its descendants.
55
+
56
+ `attrs` is a list of attribute **names**, such as `["href", "data-status"]`. It **projects** those attributes onto each match so you can read them back, and it does not filter. `["data-status=complete"]` is refused: it would otherwise look up an attribute literally named `data-status=complete`, find none, and leave the predicate resting on the locator alone, which is a green for any value the element happens to hold.
57
+
58
+ To READ the values, call `reticle_look { action: "find" }` with `attrs` and look at the `attrs` map on each match. To ASSERT one: a form field's value goes in the query as `value`, while a state expressed as a data attribute is better asserted as the consequence it stands for, such as a `signal`, a `net` call, or the text the user actually sees.
59
+
60
+ ```json
61
+ { "kind": "element", "query": { "role": "textbox", "name": "Amount", "value": "274.58" } }
62
+ ```
52
63
 
53
64
  Add `"absent": true` to assert something is **gone**. The predicate for a removal, a dismissed toast, or a regression check.
54
65
 
@@ -62,6 +73,26 @@ Add `"absent": true` to assert something is **gone**. The predicate for a remova
62
73
 
63
74
  Use `scope` to restrict the search to a CSS selector or element ref. If Reticle reports that the text is split across the scope's children, retry its generated predicate with `"self": true`; that checks the scope root's combined subtree text while still enforcing `contains`.
64
75
 
76
+ `satisfies` asserts a PROPERTY of the rendered text instead of its exact bytes: the same shape and the same properties [`state` accepts](#satisfies-asserting-a-property-instead-of-exact-bytes), applied where the user actually reads the value:
77
+
78
+ ```json
79
+ {
80
+ "kind": "text",
81
+ "scope": "[data-testid=\"release-note\"]",
82
+ "self": true,
83
+ "satisfies": { "property": "nonEmpty" }
84
+ }
85
+ ```
86
+
87
+ That is the assertion a generated feature can pass twice. `contains` demands bytes a model's output does not repeat, so it either fails on correct output or gets weakened to a word so common it proves nothing; "the box rendered and there is something in it" is the failure the feature actually has.
88
+
89
+ Two rules, both refusals rather than guesses:
90
+
91
+ - **One of `contains` or `satisfies` is required.** With neither, the predicate matches any element with any text and cannot fail.
92
+ - **`satisfies` on its own needs a `scope`** (the element whose text is read, with `self: true` for the root's own subtree). Without one the locator is every element on the page, and the property would run against whichever matched first.
93
+
94
+ Both may be given, and then both must hold. `satisfies` narrows, it never excuses.
95
+
65
96
  ### net
66
97
 
67
98
  ```json
@@ -88,7 +119,7 @@ Two fields read the RESPONSE BODY, so a request that succeeded with the wrong nu
88
119
  { "kind": "net", "urlContains": "/api/job", "bodyMatches": { "status": "completed" } }
89
120
  ```
90
121
 
91
- `bodyMatches` is a shallow field match, keyed like `signal.dataMatches` and taking the same operators (`*`, `$gte`, `$contains`). Reach for it whenever the value is an enum, because that is the case a substring cannot decide: `bodyContains: "completed"` passes on a body carrying `"completedAt": null`, where the needle matched the KEY and the job was still queued. `completed`/`completedAt`, `success`/`successRate`, `sent`/`unsent` all have that shape.
122
+ `bodyMatches` is a field match, keyed like `signal.dataMatches` and taking the same operators (`*`, `$gte`, `$contains`). A key can be a dotted path into a wrapped answer: `{ "data.status": "completed" }`, `{ "items.0.id": "a1" }`, `{ "items.length": 3 }`. A nested object written out as a value is compared whole, so use the path to match one field inside it. Reach for it whenever the value is an enum, because that is the case a substring cannot decide: `bodyContains: "completed"` passes on a body carrying `"completedAt": null`, where the needle matched the KEY and the job was still queued. `completed`/`completedAt`, `success`/`successRate`, `sent`/`unsent` all have that shape.
92
123
 
93
124
  Both require body capture, `reticle({ captureNetworkBodies: true })`, and say so when the body was never recorded rather than reporting an ordinary mismatch.
94
125
 
@@ -131,7 +162,7 @@ A passing route verdict quotes the transition itself:
131
162
  { "kind": "signal", "name": "webhook:received", "dataMatches": { "provider": "stripe", "id": "*" } }
132
163
  ```
133
164
 
134
- The app emitted a named signal via `reticle.signal()`. `dataMatches` is shallow JSON matching, and `*` means "present, any value".
165
+ The app emitted a named signal via `reticle.signal()`. `dataMatches` matches field by field, a key can be a dotted path (`"order.total"`), and `*` means "present, any value".
135
166
 
136
167
  `count` works here exactly as it does on `net`:
137
168
 
@@ -174,6 +205,72 @@ Omit `equals` entirely to assert presence. A real `$length` check against this f
174
205
 
175
206
  Note `grade: state` in the `because` line, and that the evidence collapses each row to a size marker rather than returning forty objects.
176
207
 
208
+ #### satisfies: asserting a property instead of exact bytes
209
+
210
+ `equals` cannot express output that is right differently every run. A generated summary, a classification, a computed total: each is correct without being predictable, and an assertion that demands exact bytes either fails on correct output or gets weakened until it proves nothing. `satisfies` is the other half of `state`, and it stays decidable here with no model and no network.
211
+
212
+ ```json
213
+ { "kind": "state", "store": "app", "path": "summary.text", "satisfies": { "property": "nonEmpty" } }
214
+ ```
215
+
216
+ | `property` | Extra fields | Holds when |
217
+ | --- | --- | --- |
218
+ | `nonEmpty` | none | it produced something at all, the honest floor for any output |
219
+ | `oneOf` | `values` | it landed inside the allowed set, exact membership, no coercion |
220
+ | `withinTolerance` | `of`, `tolerance` | a number near enough to the expected one, inclusive on the bound |
221
+ | `matchesPattern` | `pattern` | its string form matches that regular expression |
222
+ | `type` | `is` | it is that kind of thing; `array` and `object` are distinguished, which `typeof` cannot do |
223
+
224
+ ```json
225
+ {
226
+ "kind": "allOf",
227
+ "predicates": [
228
+ {
229
+ "kind": "state",
230
+ "path": "invoice.total",
231
+ "satisfies": { "property": "withinTolerance", "of": 274.58, "tolerance": 0.01 }
232
+ },
233
+ {
234
+ "kind": "state",
235
+ "path": "invoice.status",
236
+ "satisfies": { "property": "oneOf", "values": ["draft", "sent"] }
237
+ },
238
+ { "kind": "state", "path": "invoice.lines", "satisfies": { "property": "type", "is": "array" } }
239
+ ]
240
+ }
241
+ ```
242
+
243
+ `equals` and `satisfies` may both be given, and then both must hold. A failing check always says what it saw, because an assertion that reports only `false` sends you back for another call to find out why, and that round trip is most of what a verdict costs.
244
+
245
+ #### The relative properties: comparing with before
246
+
247
+ The properties above read the present tense. Four read the past one. They compare this value with the reading taken **before the action**, which is the window a verdict is already scoped to:
248
+
249
+ | property | holds when |
250
+ | ----------- | --------------------------- |
251
+ | `unchanged` | the value is the one it was |
252
+ | `changed` | it is not |
253
+ | `increased` | the number went up |
254
+ | `decreased` | the number went down |
255
+
256
+ ```json
257
+ {
258
+ "kind": "state",
259
+ "path": "cart.total",
260
+ "satisfies": { "property": "decreased", "by": { "op": "equals", "value": 12 } }
261
+ }
262
+ ```
263
+
264
+ `increased` and `decreased` take an optional `by`, in the protocol's own comparison vocabulary: `op` is `equals`, `at-least` or `at-most`, with an optional `tolerance` on all three. Without `by`, any movement in the right direction holds.
265
+
266
+ **Give a money assertion a tolerance.** `100 - 88.13` is `11.870000000000005`, so `{ "op": "equals", "value": 11.87 }` misses by a rounding error and reads as the app charging the wrong amount. `"tolerance": 0.01` is the fix; the failure message says so when the miss is that small.
267
+
268
+ A number is read out of displayed text, so `"$1,234.50"` compares as `1234.5`. A reading with no digits in it is **not** zero. `"sold out"` reports that nothing was subtracted rather than pretending the total fell to nothing.
269
+
270
+ These only work where a before-reading was taken, which today is `reticle_act_and_wait`. Asked anywhere else they report `unknown` with "no before-reading was taken", never a failure, because nothing was compared and a `false` would blame the app for a reading nobody took. For the same reason a relative property is never `already_true`: `unchanged` is trivially true against a baseline taken a microsecond earlier, so the pre-action check skips it and lets the action decide.
271
+
272
+ A saved flow cannot keep one yet: `FlowExpect` has no slot for `satisfies`, and rather than record a weaker assertion than the one you made, the converter records none.
273
+
177
274
  A miss quotes the real value back at you:
178
275
 
179
276
  ```json
@@ -207,6 +304,77 @@ This is the predicate that catches a UI-versus-store desync. A deploy that only
207
304
 
208
305
  Network and DOM idle. This is the deterministic replacement for a fixed sleep, not a proof of anything: combine it with a real consequence rather than asserting it alone. Omitting `until` on [`reticle_act_and_wait`](/tools/act-and-wait) waits for exactly this.
209
306
 
307
+ ### compare
308
+
309
+ ```json
310
+ {
311
+ "kind": "compare",
312
+ "left": { "from": "text", "scope": "#refunded" },
313
+ "right": { "from": "net", "urlContains": "/api/refund", "path": "refunded" },
314
+ "as": "number"
315
+ }
316
+ ```
317
+
318
+ Two observed values must agree, and you do not have to know either one. Every other predicate checks one reading against a value you write down. That cannot catch a page that echoes the user's input instead of the server's answer unless you already know the right answer: a refund of `1187.01` that the server read as paise answered `{"refunded":11.87}`, the page showed `₹1,187.01`, and every other channel was green.
319
+
320
+ Each side reads one value from a source:
321
+
322
+ | `from` | Fields | Reads |
323
+ | --- | --- | --- |
324
+ | `net` | `urlContains`, `path`, `method?`, `body?` | A field of the LAST matching call's body. `body` is `response` (default) or `request` |
325
+ | `signal` | `name`, `path` | A field of the last `name` signal's payload |
326
+ | `state` | `path`, `store?` | A store value, as `state` reads it |
327
+ | `text` | `scope` | The whole text of the element `scope` names (a CSS selector) |
328
+
329
+ A `path` is a field name or a dotted path into nested data (`data.total`, `items.0.price`), and it must end on a single value, not an object.
330
+
331
+ `as` is `value` (the default) or `number`:
332
+
333
+ - `value` is strict equality. The text `"11.87"` and the number `11.87` are different, and the failure says so and suggests `as: "number"`.
334
+ - `number` reads one number from each side. A JSON number is used as it is; a string contributes its only number, so `"Refunded ₹1,187.01"` reads as `1187.01`. A string holding two numbers is unknown rather than guessed at, so scope the text to the one value. Add `tolerance` for a band.
335
+
336
+ A side nobody could read is never a pass. A call that never happened, a missing element or a missing field fails. A body that was not recorded, was truncated before the field, or had the field redacted is unknown. Comparing a source with itself is refused before it runs, because it would agree on every page.
337
+
338
+ Two live sides (`text`, `state`) are also read before the action. If they already agreed, the result is `already_true`, because their agreement says nothing about what the action did. Put a `net` or `signal` side in the comparison to tie it to the action.
339
+
340
+ Comparing request with response on the same call is legitimate, and it is the direct check for a unit mismatch:
341
+
342
+ ```json
343
+ {
344
+ "kind": "compare",
345
+ "left": { "from": "net", "urlContains": "/api/refund", "body": "request", "path": "amount" },
346
+ "right": { "from": "net", "urlContains": "/api/refund", "path": "refunded" },
347
+ "as": "number"
348
+ }
349
+ ```
350
+
351
+ ## Asserting a failure on purpose
352
+
353
+ A fault-injection test (mock a 500, check the error UI) is asserting that a request **fails**. Say so in the predicate, or the verdict will disagree with you:
354
+
355
+ ```json
356
+ {
357
+ "kind": "allOf",
358
+ "predicates": [
359
+ { "kind": "text", "contains": "Something went wrong" },
360
+ { "kind": "net", "method": "POST", "urlContains": "/api/save", "status": 503 }
361
+ ]
362
+ }
363
+ ```
364
+
365
+ Asserting only the error text is not enough, and this is the most common way to get a confusing `verified: "no"`. Reticle watches for "the UI moved forward while a request failed", a swallowed rejection, and one of the most expensive bugs it catches. A fault-injection test looks **exactly** like that bug from the outside: a request failed, the screen changed, nothing said that was the plan.
366
+
367
+ Naming the failing call is what tells the two apart. A `net` clause with `status` of 400 or more, or `"ok": false`, which is the honest field for IPC, where there is no status code, marks that call as expected to fail, and a failure there stops being a disagreement.
368
+
369
+ <Warning>
370
+ Declare it in the top level or inside `allOf`. A declaration inside `anyOf` is deliberately
371
+ ignored: only one branch of an `anyOf` has to hold, so honouring it would suppress a real
372
+ contradiction on the strength of a branch that never ran. `not` is ignored for the same reason in
373
+ reverse: `not { net status: 500 }` asserts the call did *not* fail.
374
+ </Warning>
375
+
376
+ One case is inferred for you: an `element` or `text` clause naming an access denial ("not authorised", "sign in to continue") marks the auth statuses as expected, because a denial screen is a correct outcome rather than a fault. Everything else has to be named.
377
+
210
378
  ## Combinators
211
379
 
212
380
  ```json
@@ -226,7 +394,8 @@ Network and DOM idle. This is the deterministic replacement for a fixed sleep, n
226
394
  ```
227
395
 
228
396
  <Warning>
229
- A combinator needs its own `kind`, and the children go in `predicates`. A bare
397
+ A combinator needs its own `kind`, and the children go in `predicates`, or in `of`, which is
398
+ accepted as an alias for it. `not` takes a single child in `predicate`, or in `of`. A bare
230
399
  `{ "allOf": [ … ] }` does not parse, and neither does `{ "kind": "allOf", "allOf": [ … ] }`.
231
400
 
232
401
  Reticle refuses the call rather than running half of it. The real refusal names the field, the fix and the consequence: _"predicates: Required; unknown field allOf. Nothing ran, the predicate was not evaluated, so no verdict was produced. allOf accepts: predicates."_ Better to be told than to get a green from an assertion that never executed.
@@ -265,9 +434,9 @@ On a failure each child reports separately too, so you can see which one broke:
265
434
  | Field | Where | What it does |
266
435
  | --- | --- | --- |
267
436
  | `timeout_ms` | `assert`, `wait_for`, `act_and_wait` | Wait up to N ms for it to become true |
268
- | `since` | `net` and `console` leaves, and the call itself | Only consider events after this cursor |
437
+ | `since` | `net`, `route`, `console`, `animation` and `signal` leaves, and the call itself | Only consider events after this cursor |
269
438
 
270
- The `since` cursor comes from a prior `reticle_act` response. Use it whenever you are asserting something an action was supposed to cause. Without it, an assertion can pass on an event from two clicks ago.
439
+ The `since` cursor comes from a prior `reticle_act` response. Use it whenever you are asserting something an action was supposed to cause. Without it, an assertion can pass on an event from two clicks ago. That applies to every one of the five kinds above, not only to network calls: an unscoped `signal` matches a fire from an earlier action just as readily.
271
440
 
272
441
  <Card title="Where predicates get used" icon="circle-check" href="/tools/act-and-wait">
273
442
  Naming the consequence before the action is the whole point.
@@ -61,7 +61,7 @@ The `reticle_*` tools are already there. In your project directory:
61
61
  npx @reticlehq/server init
62
62
  ```
63
63
 
64
- Like `npm init` or `git init`, this is the per-project step: it installs the SDK, wires it into your app's entry point, restarts your dev server and then opens your app to prove a session connected. It is idempotent. Run it twice and the second run reports what was already done rather than doing it again.
64
+ Like `npm init` or `git init`, this is the per-project step: it installs the SDK, wires it into your app's entry point, starts your dev server and then opens your app to prove a session connected. It is idempotent. Run it twice and the second run reports what was already done rather than doing it again.
65
65
 
66
66
  Step 1 is once per machine. This step is once per project.
67
67
 
@@ -236,7 +236,7 @@ That third state is the one that matters. Most tools have two outcomes and quiet
236
236
  <Accordion title="How long does it actually take to install Reticle?">
237
237
  Two commands, and no restart if you run them in order. The installer registers the MCP server
238
238
  with every agent on the machine, so opening your agent afterwards is all that is needed. Then
239
- `npx @reticlehq/server init` wires the project and restarts your dev server for you. The MCP
239
+ `npx @reticlehq/server init` wires the project, starts your dev server and opens the app for you. The MCP
240
240
  registration is global, so every later project starts with the tools already there.
241
241
  </Accordion>
242
242
 
package/docs/recipes.mdx CHANGED
@@ -5,7 +5,7 @@ description: Worked examples for the situations you are actually in, with the re
5
5
  icon: book-open
6
6
  ---
7
7
 
8
- Each recipe is a situation, the call that verifies it, and what came back. Every response on this page was captured from a running app on 2026-08-16. Refs like `e605` are session-specific: take your own from [`reticle_query`](/tools/query) before copying a call.
8
+ Each recipe is a situation, the call that verifies it, and what came back. Every response on this page was captured from a running app on 2026-08-16. Refs like `e605` are session-specific: take your own from [`reticle_look { action: "find" }`](/tools/query) before copying a call.
9
9
 
10
10
  ## "Did the login actually work?"
11
11
 
@@ -135,7 +135,7 @@ The right predicate was sitting in the response the whole time:
135
135
 
136
136
  ## "Nothing matched, and I do not know why"
137
137
 
138
- An empty `reticle_query` result tells you what _is_ there:
138
+ An empty `reticle_look { action: "find" }` result tells you what _is_ there:
139
139
 
140
140
  ```json
141
141
  {
@@ -179,7 +179,7 @@ worker.onmessage = (e) => push(e.data);
179
179
  { "store": "app", "path": "cart.items.0.qty" }
180
180
  ```
181
181
 
182
- `reticle_state` walks a dot-path with numeric array indices. `depth` collapses anything deeper to a size marker, which is the difference between a cheap look and a very expensive one on a large store.
182
+ `reticle_look { action: "state" }` walks a dot-path with numeric array indices. `depth` collapses anything deeper to a size marker, which is the difference between a cheap look and a very expensive one on a large store.
183
183
 
184
184
  You can also assert on it directly, which is stronger than reading it afterwards:
185
185
 
@@ -190,7 +190,7 @@ You can also assert on it directly, which is stronger than reading it afterwards
190
190
  That catches a UI-versus-store desync, a deploy that only looks shipped, deterministically and with no model involved.
191
191
 
192
192
  <CardGroup cols={2}>
193
- <Card title="reticle_state" icon="database" href="/tools/state">
193
+ <Card title='reticle_look { action: "state" }' icon="database" href="/tools/state">
194
194
  Arguments, depth control, and reading a component's hook state.
195
195
  </Card>
196
196
  <Card title="Instrument your app" icon="wrench" href="/instrumentation">
@@ -66,7 +66,7 @@ These three were each wrong by the time somebody checked: the table read 68 / 48
66
66
 
67
67
  | Token | Produced by | Consumed by | Invalidated by |
68
68
  | --- | --- | --- | --- |
69
- | `sessionId` | `reticle_sessions`, HELLO | ~every tool | tab close; **survives reload** via `sessionStorage` |
69
+ | `sessionId` | `reticle_session { action: "list" }`, HELLO | ~every tool | tab close; **survives reload** via `sessionStorage` |
70
70
  | `ref` (`e115`) | `snapshot`, `query`, `scroll_to` | `inspect`, `act`, `act_and_wait`, `state` | any re-render; refuses cleanly rather than clicking the new occupant |
71
71
  | `actionId` (`a2`) | `act`, `act_and_wait` | `observe`, `network`, `console` filters | window expiry |
72
72
  | `since` cursor | every read | every read | ring-buffer eviction |
package/docs/telemetry.md CHANGED
@@ -122,7 +122,7 @@ Reticle verifies apps for AI agents, and for a long time it had no way to hear w
122
122
 
123
123
  | Who | How | What it carries |
124
124
  | --- | --- | --- |
125
- | Your agent | Calls the `reticle_feedback` MCP tool (after a failure, **or to ask for a feature**) | What it wrote: an analysis, or a request with the goal behind it, what would improve, and how it works around the gap today. Plus the model it is running, which it tells us because MCP cannot |
125
+ | Your agent | Calls the `reticle_session { action: "feedback" }` MCP tool (after a failure, **or to ask for a feature**) | What it wrote: an analysis, or a request with the goal behind it, what would improve, and how it works around the gap today. Plus the model it is running, which it tells us because MCP cannot |
126
126
  | You | Run `npx @reticlehq/server feedback [--rating 1-5] [--bug] "your words"` | Your words and your rating |
127
127
  | Your agent, before Reticle works | Runs `reticle feedback --agent --kind <bug\|gap\|ambiguity\|feature_request\|improvement> "what happened"` | The same words, filed from the shell. This exists for the phase where there is no daemon and no MCP tools (a failed `init`, half-finished wiring), which is the failure we are otherwise never told about |
128
128
 
@@ -4,7 +4,7 @@ description: Why asking narrow questions costs a fraction of feeding the whole a
4
4
  icon: coins
5
5
  ---
6
6
 
7
- A full Reticle verify loop costs about **100 tokens**, against roughly **7,300** for a full-tree accessibility snapshot on the same page: about 73x leaner, because Reticle asks a narrow question instead of returning the page. If you re-look after an action with `reticle_snapshot({ diff: true })`, the re-read costs about 99% less again.
7
+ A full Reticle verify loop costs about **100 tokens**, against roughly **7,300** for a full-tree accessibility snapshot on the same page: about 73x leaner, because Reticle asks a narrow question instead of returning the page. If you re-look after an action with `reticle_look({ action: "page", diff: true })`, the re-read costs about 99% less again.
8
8
 
9
9
  Agent browser tools that feed the **whole accessibility tree** to the model every step get expensive fast. Playwright MCP's own ecosystem notes its snapshots _"can exceed 50,000 tokens on complex pages,"_ with a _typical task ~114,000 tokens through MCP._ Reticle is built to ask **narrow questions** instead, so the per-interaction cost stays tiny.
10
10
 
@@ -28,7 +28,7 @@ Measured against the bench dashboard (`apps/bench-app`) **with a 1,000-item list
28
28
 
29
29
  ## Diffed snapshots: pay once, then only for changes
30
30
 
31
- After the first snapshot, pass `reticle_snapshot({ diff: true })` to get back **only what changed** since your last look of the same scope/mode (`mode:delta` with added/removed lines, or `mode:unchanged`). A route change auto-resets to a full snapshot, so you never read a misleading cross-page diff.
31
+ After the first snapshot, pass `reticle_look({ action: "page", diff: true })` to get back **only what changed** since your last look of the same scope/mode (`mode:delta` with added/removed lines, or `mode:unchanged`). A route change auto-resets to a full snapshot, so you never read a misleading cross-page diff.
32
32
 
33
33
  Measured on a representative 150-row dashboard (the shipped regression benchmark `server/src/surface/tools/snapshot-cost.test.ts`, char/4 proxy):
34
34
 
@@ -40,7 +40,7 @@ Measured on a representative 150-row dashboard (the shipped regression benchmark
40
40
 
41
41
  **~99% fewer tokens** to re-look after an action, and because a `delta` carries no stale full tree, it also removes the 60K to 80K-token stale-context buildup that makes long-running agents start hallucinating selectors that no longer exist.
42
42
 
43
- Every `reticle_snapshot`/`reticle_query` result also carries `cost:{ bytes, tokens }` (estimated) so you can **re-scope before reading** a large body (`mode:interactive`/`status`, a tighter `scope`, or a narrower `query`) instead of paying for it first.
43
+ Every `reticle_look { action: "page" }`/`reticle_look { action: "find" }` result also carries `cost:{ bytes, tokens }` (estimated) so you can **re-scope before reading** a large body (`mode:interactive`/`status`, a tighter `scope`, or a narrower `query`) instead of paying for it first.
44
44
 
45
45
  ## The other tax: tool schemas, paid on every request
46
46
 
@@ -162,6 +162,7 @@ A verification tool that cannot describe the quality of its own evidence is aski
162
162
  | `predicate` | Alias for `until`, the name `assert` and `wait_for` use |
163
163
  | `timeout_ms` | Maximum wait, defaulting to 4000. `0` evaluates the predicate once, no wait |
164
164
  | `refuseWhenThrottled` | Throw rather than act on a throttled tab |
165
+ | `durable` | After a `yes`, reload the page and require the element, text and state parts of `until` to hold again. A consequence that is gone after the reload turns the verdict into `no`: the UI advanced, the change did not persist |
165
166
 
166
167
  Omitting `until` waits for network and DOM idle, which is the deterministic replacement for a fixed sleep. To assert a consequence **and** settle, combine them:
167
168
 
@@ -5,7 +5,7 @@ icon: layer-group
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_act_sequence is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_act_sequence is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_act { steps: [...] }`** instead: the same call, routed on the steps array. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
@@ -5,7 +5,7 @@ icon: terminal
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_console is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_console is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_observe { action: "console" }`** instead. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
@@ -5,7 +5,7 @@ icon: crosshairs
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_inspect is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_inspect is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_look { action: "element" }`** instead. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
@@ -5,7 +5,7 @@ icon: satellite-dish
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_network is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_network is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_observe { action: "network" }`** instead. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
@@ -5,7 +5,7 @@ icon: magnifying-glass
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_query is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_query is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_look { action: "find" }`** instead. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
@@ -52,16 +52,16 @@ Seven buttons, each with a `ref` to act on and a source file to open, for 236 to
52
52
 
53
53
  ## Arguments
54
54
 
55
- | Argument | What it does |
56
- | ------------ | -------------------------------------------------------------------------- |
57
- | `by` | `role` · `text` · `label` · `placeholder` · `testid` · `alt` · `component` |
58
- | `value` | The query value for that strategy |
59
- | `name` | Accessible-name filter. Narrows a broad `role` query |
60
- | `scope` | CSS selector or ref, to search inside a subtree |
61
- | `limit` | Cap the descriptors returned. Cuts tokens hard on broad queries |
62
- | `count_only` | Return just `{ count }`, around 30x smaller |
63
- | `attrs` | Extra attributes per match, e.g. `['href']` to inventory links |
64
- | `self` | Return the `scope` element itself instead of searching inside it |
55
+ | Argument | What it does |
56
+ | --- | --- |
57
+ | `by` | `role` · `text` · `label` · `placeholder` · `testid` · `alt` · `component` |
58
+ | `value` | The query value for that strategy |
59
+ | `name` | Accessible-name filter. Narrows a broad `role` query |
60
+ | `scope` | CSS selector or ref, to search inside a subtree |
61
+ | `limit` | Cap the descriptors returned. Cuts tokens hard on broad queries |
62
+ | `count_only` | Return just `{ count }`, around 30x smaller |
63
+ | `attrs` | Attribute NAMES to project per match, e.g. `['href']` to inventory links. The values come back in each match's `attrs` map. It does not filter, so `name=value` is refused |
64
+ | `self` | Return the `scope` element itself instead of searching inside it |
65
65
 
66
66
  Every argument also has a predicate spelling, so `{ "role": "button" }` works as shorthand for `{ "by": "role", "value": "button" }`.
67
67
 
@@ -5,7 +5,7 @@ icon: list-check
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_sessions is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_sessions is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_session { action: "list" }`** instead. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
@@ -5,7 +5,7 @@ icon: camera
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_snapshot is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_snapshot is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_look { action: "page" }`** instead. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
@@ -5,7 +5,7 @@ icon: database
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_state is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_state is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_look { action: "state" }`** instead. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
@@ -5,7 +5,7 @@ icon: hourglass
5
5
  ---
6
6
 
7
7
  <Warning>
8
- **reticle_wait_for is not advertised.** The default surface is the merged nine, so an agent does not see
8
+ **reticle_wait_for is not advertised.** The default surface is the merged ten, so an agent does not see
9
9
  this name. Call **`reticle_assert { action: "wait" }`** instead. Everything below describes what that call does;
10
10
  only the spelling changed. A call to the old name is answered with the new one, but an
11
11
  `allowedTools` allowlist or an MCP permission rule naming it refuses before Reticle is asked.
package/docs/usage.md CHANGED
@@ -4,7 +4,7 @@ description: 'The full reference and cookbook: every tool, flag, and workflow, w
4
4
  icon: book
5
5
  ---
6
6
 
7
- Every Reticle drive is the same four steps: **look** (`reticle_look { action: "page" }` / `reticle_look { action: "find" }`), **act** (`reticle_act` / `reticle_act { steps: [...] }`), **observe** (`reticle_observe` / `reticle_observe { action: "network" }` / `reticle_look { action: "state" }`), **assert** (`reticle_assert`). Only `reticle_act_and_wait` and `reticle_assert` produce a verdict, so a run that ends anywhere else proved nothing. This page is the full reference for every tool, predicate, action and flag in that loop.
7
+ Every Reticle drive is the same four steps: **look** (`reticle_look { action: "page" }` / `reticle_look { action: "find" }`), **act** (`reticle_act` / `reticle_act { steps: [...] }`), **observe** (`reticle_observe` / `reticle_observe { action: "network" }` / `reticle_look { action: "state" }`), **assert** (`reticle_assert`). Verdicts come from `reticle_act_and_wait`, `reticle_assert`, and `reticle_act { steps }` when a step declares `expect`, so a run that ends anywhere else proved nothing. This page is the full reference for every tool, predicate, action and flag in that loop.
8
8
 
9
9
  If you haven't set up Reticle yet, start with [Getting Started](getting-started.md).
10
10
 
@@ -183,7 +183,7 @@ The timeline + summary of what happened.
183
183
 
184
184
  Act, then wait for a predicate: the whole act→observe→assert loop in one hop.
185
185
 
186
- - **args:** `ref`, `action`, `args?`, `until: <predicate>`, `timeout_ms?` (default 4000; 0 = evaluate once), `refuseWhenThrottled?`, `intent?`, `sessionId?`.
186
+ - **args:** `ref`, `action`, `args?`, `until: <predicate>`, `timeout_ms?` (default 4000; 0 = evaluate once), `refuseWhenThrottled?`, `intent?`, `durable?` (after a yes, reload and require `until`'s element/text/state parts to hold again), `sessionId?`.
187
187
  - **returns:** `{ effect, verdict, trace, session, warning? }`. `effect` is the action result (`{ ok, ref, action }`), `verdict` is `{ pass, evidence?, failureReason? }`, `trace` is the reaction report of everything the app did after the action, and `session` is the tab-health block `{ lastSeenMs, throttled, focused }` (with a `warning` when throttled). A failing `verdict` still returns `effect` + `trace` so you can see what _did_ happen. The predicate is automatically floored at this act's cursor, so it only matches events the action actually caused.
188
188
 
189
189
  ### `reticle_assert { action: "wait" }`
@@ -200,7 +200,7 @@ Verify a predicate; optionally wait for it.
200
200
  - **args:** `predicate`, `timeout_ms?` (0 = evaluate once), `since?`, `intent?`, `sessionId?`.
201
201
  - Same `since` default as `reticle_assert { action: "wait" }`: scoped to your last act so a stale buffered event can't fake a pass; override with an explicit `since`.
202
202
  - **returns:** `{ verified, because, pass, evidence, contradictions?, coverage?, failureReason?, session, warning? }`. On failure includes a **near-miss** (e.g. "found the dialog but not visible", or "no button named 'Submit'; saw: Cancel"). The `session` block `{ lastSeenMs, throttled, focused }` reports tab health on every assert; when throttled a `warning` is attached so you never assert against a tab that is silently no-oping.
203
- - **`intent` declares what the change was FOR, inline.** Pass a sentence in your own words and it lands in `.reticle/intent.json`, the same git-checked ledger `reticle_intent` writes, before the verdict is drawn; a green verdict then marks it proved and names itself as the proof. Pass the **id** of an intent you already declared instead, to point several verdicts at one statement rather than restating it. `reticle_act_and_wait` takes the same argument. Omit it and nothing is written.
203
+ - **`intent` declares what the change was FOR, inline.** Pass a sentence in your own words and it lands in `.reticle/intent/` (one directory per subject, a flow's name being its subject), the same git-checked ledger `reticle_intent` writes, before the verdict is drawn; a green verdict then marks it proved and names itself as the proof. Pass the **id** of an intent you already declared instead, to point several verdicts at one statement rather than restating it. `reticle_act_and_wait` takes the same argument. Omit it and nothing is written.
204
204
  - **Read `verified`, not `pass`.** `pass` says the predicate held; `verified` says whether that means anything. It is `"no"` when a channel contradicts the assertion (a failed write under a green screen, a batch whose body reports per-item failures, a request still in flight), and `"unknown"` when the outcome could not be known yet: a `202 Accepted` that has not reconciled, or a write whose response body was never recorded. `because` names the deciding evidence in one sentence.
205
205
 
206
206
  ### `reticle_reconcile`
@@ -350,7 +350,7 @@ A **predicate** declares what should be true. `reticle_assert` / `reticle_assert
350
350
  // An element exists / is in a state
351
351
  { "kind": "element", "query": { "role": "dialog", "name": "Confirm" }, "state": "visible" }
352
352
  // query supports: role, name, text, label, placeholder, testid, alt, scope
353
- // state: visible | hidden | enabled | disabled | checked | expanded | focused | present | inViewport
353
+ // state: visible | hidden | enabled | disabled | checked | expanded | pressed | focused | present | inViewport
354
354
  // inViewport asserts the element is in the viewport NOW (not just in the DOM), so a scrollIntoView is gradeable
355
355
  // add "absent": true to assert it is NOT there (regression / removal)
356
356
 
@@ -396,7 +396,7 @@ A `state` assertion is graded as a **consequence** (a wrong element or stale ren
396
396
  - `timeout_ms` (on `assert`/`wait_for`): wait up to N ms for it to become true.
397
397
  - `since` (on `net`/`console` leaves): only consider events after this cursor (from `act`).
398
398
 
399
- `dataMatches` uses shallow JSON matching; `*` means "present, any value".
399
+ `dataMatches` matches field by field; a key can be a dotted path (`"order.total"`, `"items.0.id"`), and `*` means "present, any value".
400
400
 
401
401
  ---
402
402
 
@@ -640,7 +640,7 @@ Capture everything that happens across a span. Useful for "run my whole checkout
640
640
 
641
641
  ```jsonc
642
642
  reticle_record {action:"start"}({ recordingName: "checkout" })
643
- // …agent performs the flow (reticle_act / reticle_act_sequence)…
643
+ // …agent performs the flow (reticle_act, single or with steps)…
644
644
  reticle_record {action:"stop"}({ recordingName: "checkout" })
645
645
  // → {
646
646
  // recordingName,
@@ -732,7 +732,7 @@ No, for basic look/act/observe. You'll get better results by adding `data-testid
732
732
 
733
733
  ### Does it work without React?
734
734
 
735
- The core (DOM/network/route/console/animation/snapshot/actions) is framework-agnostic and is gated against a vanilla-TS app. React, Next.js, Remix and Astro each have an app and a CI gate. SvelteKit is wired end-to-end. `reticle init` writes the client hook and the Vite plugin, and the plugin stamps `data-reticle-source` into `.svelte` components so verdicts carry `file:line`. But there is still no SvelteKit app in CI, so it is unverified rather than supported. Vue has a Pinia store adapter and nothing else: no detection, no `.vue` stamping, no gate. See [what Svelte support is and is not](getting-started.md#what-svelte-support-is-and-what-it-is-not).
735
+ The core (DOM/network/route/console/animation/snapshot/actions) is framework-agnostic and is gated against a vanilla-TS app. What is proven for each framework, from driven to a verdict in CI to install-gated to wired but unverified, is listed once in [Frameworks](frameworks.mdx). See also [what Svelte support is and is not](getting-started.md#what-svelte-support-is-and-what-it-is-not).
736
736
 
737
737
  ### Can it judge whether my UI looks good?
738
738
 
@@ -858,6 +858,7 @@ It does **not** freeze `requestAnimationFrame`/microtasks (React's scheduler kee
858
858
  "dataMatches": { "count": { "$gte": 1 }, "sections": { "$contains": "hook" } },
859
859
  }
860
860
  // operators: $gte $lte $gt $lt $contains (array/substring) $length ; "*" = present
861
+ // keys: a field name, or a dotted path into nested data ("order.total", "items.0.id")
861
862
  ```
862
863
 
863
864
  On a failed signal assert, the result includes a **near-miss**: the signals that _did_ fire with that name + their data. And `reticle_observe`'s summary now includes `domChanged` (in-place text/attribute re-renders, not just added/removed nodes).