@reticlehq/server 3.3.0 → 3.5.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 (326) hide show
  1. package/LICENSE-ENTERPRISE +1 -1
  2. package/README.md +3 -3
  3. package/SKILL.md +12 -14
  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 +22 -0
  7. package/dist/command/cli/adhoc-suite.js +90 -19
  8. package/dist/command/cli/adhoc-verdict.d.ts +35 -0
  9. package/dist/command/cli/adhoc-verdict.js +90 -3
  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 +6 -2
  15. package/dist/command/cli/auth/cloud-session.js +2 -0
  16. package/dist/command/cli/auth/device-flow.d.ts +2 -2
  17. package/dist/command/cli/cli-doctor.js +7 -5
  18. package/dist/command/cli/cli-flow-commands.d.ts +3 -6
  19. package/dist/command/cli/cli-flow-commands.js +67 -4
  20. package/dist/command/cli/cli-parse.d.ts +8 -1
  21. package/dist/command/cli/cli-parse.js +24 -4
  22. package/dist/command/cli/cli-update-commands.js +8 -1
  23. package/dist/command/cli/cli-verify.d.ts +24 -3
  24. package/dist/command/cli/cli-verify.js +54 -11
  25. package/dist/command/cli/cloud-cli.js +64 -2
  26. package/dist/command/cli/cloud-kit.js +3 -5
  27. package/dist/command/cli/cloud-login.d.ts +1 -1
  28. package/dist/command/cli/cloud-login.js +27 -8
  29. package/dist/command/cli/demo/demo-page.js +6 -3
  30. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
  31. package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
  32. package/dist/command/cli/launch/cli-launch.d.ts +8 -0
  33. package/dist/command/cli/launch/cli-launch.js +21 -9
  34. package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
  35. package/dist/command/cli/launch/sdk-version-match.js +206 -0
  36. package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
  37. package/dist/command/cli/ports/resolve/cli-port.d.ts +25 -0
  38. package/dist/command/cli/ports/resolve/cli-port.js +125 -15
  39. package/dist/command/cli/setup-mcp-cli.js +21 -13
  40. package/dist/command/cli/tutorial.d.ts +2 -2
  41. package/dist/command/cli/tutorial.js +21 -7
  42. package/dist/command/cli.js +79 -11
  43. package/dist/command/daemon/binding/daemon-status-probe.d.ts +14 -2
  44. package/dist/command/daemon/binding/daemon-status-probe.js +34 -6
  45. package/dist/command/daemon/daemon-resolve.d.ts +9 -0
  46. package/dist/command/daemon/daemon-resolve.js +1 -1
  47. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
  48. package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
  49. package/dist/command/dev/stale-issue-guard.d.ts +3 -4
  50. package/dist/command/dev/stale-issue-guard.js +8 -8
  51. package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
  52. package/dist/command/setup/bringup/connect-lease.js +38 -0
  53. package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
  54. package/dist/command/setup/bringup/dev-server-files.js +131 -0
  55. package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
  56. package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
  57. package/dist/command/setup/bringup/dev-server-wait.d.ts +2 -0
  58. package/dist/command/setup/bringup/dev-server-wait.js +1 -1
  59. package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
  60. package/dist/command/setup/bringup/ensure-daemon.js +135 -16
  61. package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
  62. package/dist/command/setup/bringup/static-page-server.js +151 -0
  63. package/dist/command/setup/desktop-shape.d.ts +4 -0
  64. package/dist/command/setup/desktop-shape.js +18 -2
  65. package/dist/command/setup/init/init-port.d.ts +15 -0
  66. package/dist/command/setup/init/init-port.js +44 -0
  67. package/dist/command/setup/init/init-runtime.d.ts +1 -1
  68. package/dist/command/setup/init/init-runtime.js +91 -9
  69. package/dist/command/setup/node-effects.d.ts +27 -14
  70. package/dist/command/setup/node-effects.js +86 -45
  71. package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
  72. package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
  73. package/dist/command/setup/probe/page-probe.d.ts +13 -2
  74. package/dist/command/setup/probe/page-probe.js +18 -3
  75. package/dist/command/setup/run-setup.d.ts +28 -1
  76. package/dist/command/setup/run-setup.js +103 -15
  77. package/dist/command/setup/session-pick.d.ts +13 -0
  78. package/dist/command/setup/session-pick.js +20 -0
  79. package/dist/command/setup/setup-command.d.ts +2 -14
  80. package/dist/command/setup/setup-command.js +105 -31
  81. package/dist/command/setup/setup-mcp.d.ts +2 -0
  82. package/dist/command/setup/setup-mcp.js +17 -8
  83. package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
  84. package/dist/command/setup/terminal/wait-progress.js +17 -0
  85. package/dist/command/version/peer-announce.d.ts +6 -2
  86. package/dist/command/version/peer-announce.js +8 -6
  87. package/dist/command/version/version-nudge.d.ts +17 -3
  88. package/dist/command/version/version-nudge.js +25 -4
  89. package/dist/features/crawl/crawl-empty.js +1 -1
  90. package/dist/features/crawl/crawl-tools.d.ts +6 -0
  91. package/dist/features/crawl/crawl-tools.js +57 -0
  92. package/dist/features/crawl/crawl.js +171 -157
  93. package/dist/features/exhaust/app-coverage.d.ts +33 -0
  94. package/dist/features/exhaust/app-coverage.js +31 -0
  95. package/dist/features/exhaust/code-coverage.d.ts +47 -0
  96. package/dist/features/exhaust/code-coverage.js +77 -0
  97. package/dist/features/exhaust/exhaust-run.d.ts +33 -0
  98. package/dist/features/exhaust/exhaust-run.js +82 -0
  99. package/dist/features/exhaust/explorer.d.ts +103 -0
  100. package/dist/features/exhaust/explorer.js +195 -0
  101. package/dist/features/exhaust/failure-branches.d.ts +33 -0
  102. package/dist/features/exhaust/failure-branches.js +92 -0
  103. package/dist/features/exhaust/ledger.d.ts +202 -0
  104. package/dist/features/exhaust/ledger.js +252 -0
  105. package/dist/features/exhaust/session-fold.d.ts +31 -0
  106. package/dist/features/exhaust/session-fold.js +79 -0
  107. package/dist/features/exhaust/session-port.d.ts +36 -0
  108. package/dist/features/exhaust/session-port.js +102 -0
  109. package/dist/features/harness/drive-report.d.ts +0 -9
  110. package/dist/features/harness/drive-report.js +18 -0
  111. package/dist/features/harness/driver.js +4 -7
  112. package/dist/features/harness/fill-values.d.ts +10 -0
  113. package/dist/features/harness/fill-values.js +50 -11
  114. package/dist/features/harness/jev-driver.js +30 -13
  115. package/dist/features/harness/openai-driver.d.ts +12 -0
  116. package/dist/features/harness/openai-driver.js +21 -12
  117. package/dist/features/harness/platform-config.js +5 -6
  118. package/dist/features/license/license.d.ts +2 -2
  119. package/dist/index.d.ts +9 -0
  120. package/dist/index.js +56 -19
  121. package/dist/judgement/capsule/capsule-store.d.ts +4 -4
  122. package/dist/judgement/domain/domain-model.js +3 -1
  123. package/dist/judgement/oracles/self-instrument.js +4 -2
  124. package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
  125. package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
  126. package/dist/judgement/runs/verification-sync.d.ts +3 -1
  127. package/dist/judgement/runs/verification-sync.js +1 -1
  128. package/dist/language/flows/change/affected.d.ts +9 -0
  129. package/dist/language/flows/change/affected.js +35 -0
  130. package/dist/language/flows/change/gate.d.ts +20 -0
  131. package/dist/language/flows/change/gate.js +12 -1
  132. package/dist/language/flows/flow-author.d.ts +7 -0
  133. package/dist/language/flows/flow-author.js +19 -0
  134. package/dist/language/flows/flow-expect-grammar.js +1 -1
  135. package/dist/language/flows/flow-journey.d.ts +54 -0
  136. package/dist/language/flows/flow-journey.js +119 -0
  137. package/dist/language/flows/flow-replay-run.d.ts +2 -3
  138. package/dist/language/flows/flow-replay-run.js +13 -47
  139. package/dist/language/flows/flow-replay.d.ts +22 -8
  140. package/dist/language/flows/flow-replay.js +55 -9
  141. package/dist/language/flows/flow-result.d.ts +2 -0
  142. package/dist/language/flows/flow-result.js +25 -0
  143. package/dist/language/flows/flow-tools.js +17 -5
  144. package/dist/language/flows/flows.d.ts +10 -1
  145. package/dist/language/flows/flows.js +90 -14
  146. package/dist/language/flows/recording/replay-halt.js +3 -1
  147. package/dist/language/flows/recording/tape/recordings.d.ts +30 -1
  148. package/dist/language/flows/recording/tape/recordings.js +55 -1
  149. package/dist/language/flows/replay.d.ts +19 -0
  150. package/dist/language/flows/replay.js +42 -6
  151. package/dist/language/flows/verify-change-tools.js +5 -2
  152. package/dist/launch-chromium.d.ts +80 -0
  153. package/dist/launch-chromium.js +218 -0
  154. package/dist/memory/cloud/cloud-config.d.ts +11 -2
  155. package/dist/memory/cloud/cloud-config.js +47 -7
  156. package/dist/memory/cloud/cloud-sync.d.ts +4 -1
  157. package/dist/memory/cloud/cloud-sync.js +6 -9
  158. package/dist/memory/cloud/harness-offer.js +5 -6
  159. package/dist/memory/cloud/harness-switch.js +5 -6
  160. package/dist/memory/cloud/sync-cycle.d.ts +77 -3
  161. package/dist/memory/cloud/sync-cycle.js +325 -55
  162. package/dist/memory/cloud/sync-daemon.js +24 -20
  163. package/dist/memory/cloud/sync-disk.js +16 -2
  164. package/dist/memory/intent/intent-shard.d.ts +10 -32
  165. package/dist/memory/intent/intent-shard.js +0 -10
  166. package/dist/memory/intent/intent-tools.js +8 -3
  167. package/dist/memory/journal/drive-flow.d.ts +18 -13
  168. package/dist/memory/journal/drive-flow.js +45 -37
  169. package/dist/memory/journal/envelope.d.ts +8 -8
  170. package/dist/memory/journal/journal-query.d.ts +14 -0
  171. package/dist/memory/journal/journal-query.js +15 -0
  172. package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
  173. package/dist/memory/journal/session-end.d.ts +9 -1
  174. package/dist/memory/journal/session-end.js +24 -12
  175. package/dist/memory/project/artifact-root-resolver.d.ts +10 -0
  176. package/dist/memory/project/artifact-root-resolver.js +46 -19
  177. package/dist/memory/project/dir/reticle-dir.d.ts +2 -0
  178. package/dist/memory/project/dir/reticle-dir.js +1 -0
  179. package/dist/memory/project/project-tools.js +3 -1
  180. package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
  181. package/dist/portal/bridge/auth-failure-reason.js +4 -2
  182. package/dist/portal/bridge/bridge.d.ts +12 -3
  183. package/dist/portal/bridge/bridge.js +49 -3
  184. package/dist/portal/input/js-coverage.d.ts +25 -0
  185. package/dist/portal/input/js-coverage.js +30 -0
  186. package/dist/portal/input/real-input.d.ts +11 -0
  187. package/dist/portal/input/real-input.js +29 -4
  188. package/dist/portal/input/viewport-tools.js +16 -6
  189. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  190. package/dist/portal/pool/playwright-launcher.js +20 -13
  191. package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
  192. package/dist/portal/session/dev-server/dev-command.js +30 -12
  193. package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
  194. package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
  195. package/dist/portal/session/dev-server/route-status-probe.js +57 -0
  196. package/dist/portal/session/facts/observed-state.d.ts +11 -0
  197. package/dist/portal/session/facts/observed-state.js +34 -0
  198. package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
  199. package/dist/portal/session/no-session-diagnosis.js +62 -5
  200. package/dist/portal/session/no-session-next-action.d.ts +13 -0
  201. package/dist/portal/session/no-session-next-action.js +48 -9
  202. package/dist/portal/session/no-session-watch.d.ts +18 -0
  203. package/dist/portal/session/no-session-watch.js +88 -2
  204. package/dist/portal/session/session-health.js +3 -2
  205. package/dist/portal/session/session-manager.d.ts +20 -6
  206. package/dist/portal/session/session-manager.js +19 -3
  207. package/dist/portal/session/session-reaper.d.ts +28 -0
  208. package/dist/portal/session/session-reaper.js +49 -1
  209. package/dist/portal/session/session.d.ts +10 -8
  210. package/dist/portal/session/session.js +24 -32
  211. package/dist/surface/http-server.d.ts +3 -2
  212. package/dist/surface/http-server.js +8 -5
  213. package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
  214. package/dist/surface/mcp/mcp-proxy.js +2 -2
  215. package/dist/surface/mcp/mcp.js +5 -3
  216. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
  217. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
  218. package/dist/surface/mcp/proxy/proxy-log.js +3 -5
  219. package/dist/surface/mcp/server-instructions.js +9 -7
  220. package/dist/surface/tools/act/after-match.d.ts +17 -0
  221. package/dist/surface/tools/act/after-match.js +30 -0
  222. package/dist/surface/tools/act/durable.d.ts +69 -0
  223. package/dist/surface/tools/act/durable.js +139 -0
  224. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +13 -6
  225. package/dist/surface/tools/act/navigation/navigate-arrival.js +47 -10
  226. package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
  227. package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
  228. package/dist/surface/tools/act/proved-controls.d.ts +9 -0
  229. package/dist/surface/tools/act/proved-controls.js +11 -0
  230. package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
  231. package/dist/surface/tools/act/sequence-grade.js +32 -1
  232. package/dist/surface/tools/act-capsule.d.ts +7 -0
  233. package/dist/surface/tools/act-capsule.js +46 -1
  234. package/dist/surface/tools/act-sequence-tool.js +25 -8
  235. package/dist/surface/tools/act-tools.js +28 -52
  236. package/dist/surface/tools/assert/assert-grade.js +12 -1
  237. package/dist/surface/tools/assert/assert-verdict.js +2 -2
  238. package/dist/surface/tools/browser-tools.js +14 -2
  239. package/dist/surface/tools/coverage-tools.js +27 -1
  240. package/dist/surface/tools/dynamic-tools.js +29 -2
  241. package/dist/surface/tools/error-recovery.js +5 -6
  242. package/dist/surface/tools/feedback-invite.js +6 -4
  243. package/dist/surface/tools/harness-explore.d.ts +3 -2
  244. package/dist/surface/tools/harness-explore.js +12 -7
  245. package/dist/surface/tools/invoke-tool.js +12 -8
  246. package/dist/surface/tools/lease-tools.js +15 -6
  247. package/dist/surface/tools/live-call-text.js +4 -1
  248. package/dist/surface/tools/observe-tools.js +9 -1
  249. package/dist/surface/tools/tool-kit.d.ts +10 -0
  250. package/dist/surface/tools/tools.js +4 -0
  251. package/dist/telemetry/argument-shape.d.ts +0 -2
  252. package/dist/telemetry/argument-shape.js +1 -5
  253. package/dist/telemetry/error-class.d.ts +2 -3
  254. package/dist/telemetry/error-class.js +2 -3
  255. package/dist/telemetry/harness-drive.d.ts +0 -2
  256. package/dist/telemetry/harness-drive.js +0 -4
  257. package/dist/telemetry/onboarding-firsts.d.ts +2 -2
  258. package/dist/telemetry/onboarding-firsts.js +2 -2
  259. package/dist/wire-journal.d.ts +3 -0
  260. package/dist/wire-journal.js +4 -0
  261. package/dist/workspace-packages.d.ts +0 -2
  262. package/dist/workspace-packages.js +1 -9
  263. package/docs/README.md +4 -2
  264. package/docs/actions.mdx +3 -4
  265. package/docs/agent-cheatsheet.md +4 -4
  266. package/docs/architecture.md +6 -6
  267. package/docs/best-practices.mdx +1 -1
  268. package/docs/capabilities.mdx +32 -1
  269. package/docs/cli/cloud.mdx +20 -8
  270. package/docs/cli/doctor.mdx +4 -0
  271. package/docs/cli/feedback.mdx +2 -2
  272. package/docs/cli/gate.mdx +9 -4
  273. package/docs/cli/update.mdx +10 -0
  274. package/docs/cli.mdx +22 -7
  275. package/docs/desktop.mdx +14 -10
  276. package/docs/environment.mdx +87 -0
  277. package/docs/faq.mdx +18 -16
  278. package/docs/first-drive.md +5 -13
  279. package/docs/flows.md +21 -2
  280. package/docs/frameworks.mdx +29 -10
  281. package/docs/getting-started.md +15 -5
  282. package/docs/http-transport.md +6 -3
  283. package/docs/human-control.md +2 -2
  284. package/docs/index.mdx +16 -6
  285. package/docs/install-agentic.mdx +2 -2
  286. package/docs/install-manual.mdx +1 -1
  287. package/docs/integration-patterns.md +3 -3
  288. package/docs/multi-agent-testing.md +1 -1
  289. package/docs/packages/babel-plugin.mdx +1 -1
  290. package/docs/packages/browser.mdx +2 -2
  291. package/docs/packages/next.mdx +3 -0
  292. package/docs/packages/react.mdx +2 -2
  293. package/docs/packages/tauri.mdx +1 -1
  294. package/docs/packages/vite-plugin.mdx +4 -2
  295. package/docs/packages.mdx +9 -9
  296. package/docs/predicates.mdx +50 -5
  297. package/docs/quickstart.mdx +12 -10
  298. package/docs/recipes.mdx +2 -2
  299. package/docs/state-management.mdx +2 -2
  300. package/docs/system-map.md +1 -1
  301. package/docs/telemetry.md +1 -1
  302. package/docs/token-efficiency.md +3 -3
  303. package/docs/tools/act-and-wait.mdx +1 -0
  304. package/docs/tools/act-sequence.mdx +1 -1
  305. package/docs/tools/act.mdx +1 -1
  306. package/docs/tools/console.mdx +1 -1
  307. package/docs/tools/inspect.mdx +1 -1
  308. package/docs/tools/network.mdx +1 -1
  309. package/docs/tools/query.mdx +1 -1
  310. package/docs/tools/sessions.mdx +1 -1
  311. package/docs/tools/snapshot.mdx +1 -1
  312. package/docs/tools/state.mdx +1 -1
  313. package/docs/tools/wait-for.mdx +1 -1
  314. package/docs/usage.md +7 -7
  315. package/docs/use-cases.mdx +157 -0
  316. package/docs/vs-screenshots.mdx +3 -3
  317. package/docs/what-is-recorded.md +2 -2
  318. package/docs/what-is-reticle.mdx +17 -20
  319. package/docs/why-reticle.mdx +14 -21
  320. package/package.json +10 -9
  321. package/dist/judgement/capsule/minimize.d.ts +0 -12
  322. package/dist/judgement/capsule/minimize.js +0 -22
  323. package/dist/judgement/phenomena/phenomena.d.ts +0 -27
  324. package/dist/judgement/phenomena/phenomena.js +0 -101
  325. package/dist/language/flows/flow-report.d.ts +0 -22
  326. package/dist/language/flows/flow-report.js +0 -106
@@ -46,18 +46,10 @@ If `~/.reticle/pairing-token` does not exist yet, run any `reticle` command once
46
46
 
47
47
  This is the shortest path to seeing Reticle work, and it needs no MCP client at all.
48
48
 
49
- Open the app in a browser. Use a real one, or:
49
+ Ask for a verdict, naming the app's url. With no tab open on that origin, Reticle opens one in a browser it drives (Playwright's Chromium, or an installed Chrome or Edge when that is missing) and closes it afterwards; a tab you already have open is used instead:
50
50
 
51
51
  ```bash
52
- node -e "require('playwright').chromium.launch({headless:true})
53
- .then(b=>b.newPage().then(p=>p.goto('http://localhost:4310/')
54
- .then(()=>new Promise(r=>setTimeout(r,60000)))))" &
55
- ```
56
-
57
- Then ask for a verdict:
58
-
59
- ```bash
60
- node server/dist/command/cli.js verify "" --port 4400 \
52
+ node server/dist/command/cli.js verify http://localhost:4310/ --port 4400 \
61
53
  --expect '{"kind":"text","contains":"Reticle"}'
62
54
  ```
63
55
 
@@ -72,7 +64,7 @@ That is the whole product in one line: a claim, checked against a running page,
72
64
  Try making it fail, because a tool that only ever says yes is not telling you anything:
73
65
 
74
66
  ```bash
75
- node server/dist/command/cli.js verify "" --port 4400 \
67
+ node server/dist/command/cli.js verify http://localhost:4310/ --port 4400 \
76
68
  --expect '{"kind":"text","contains":"definitely not on this page"}'
77
69
  ```
78
70
 
@@ -80,8 +72,8 @@ node server/dist/command/cli.js verify "" --port 4400 \
80
72
 
81
73
  If your editor has the Reticle MCP server, the same app is now drivable. The four calls worth knowing, in the order you actually use them:
82
74
 
83
- - `reticle_snapshot`: what is on the page
84
- - `reticle_query { by: "testid", value: "..." }`: find one thing, cheaply
75
+ - `reticle_look { action: "page" }`: what is on the page
76
+ - `reticle_look { action: "find", by: "testid", value: "..." }`: find one thing, cheaply
85
77
  - `reticle_act_and_wait { ref, action, until }` (**the one that produces a verdict**)
86
78
  - `reticle_observe`: everything the page did, when you need the evidence
87
79
 
package/docs/flows.md CHANGED
@@ -87,6 +87,25 @@ reticle_flow {action:"load"}({ flowName: "create-task" }) // → the flow JSON
87
87
  reticle_flow_replay({ flowName: "create-task" }) // re-resolve each anchor against the LIVE DOM, run it
88
88
  ```
89
89
 
90
+ ### Resume from a step
91
+
92
+ ```jsonc
93
+ reticle_flow_replay({ flowName: "create-task", from: 3 }) // by 0-based index
94
+ reticle_flow_replay({ flowName: "create-task", from: "submit" }) // or by a step's `id`
95
+ ```
96
+
97
+ There is no state to restore, so the steps before `from` run again quickly as setup: their actions run, their `expect`s are not checked, and they are not reported. Replay is then checked and reported from `from` on. If a setup step fails, it is reported, because the resume never reached the step you asked for. A setup step declared `"effect": "commits"` refuses the resume and the whole flow replays, so resuming never re-sends a payment or a message silently. A `from` that names no step is an error, not a full replay.
98
+
99
+ ## Flows saved from a drive
100
+
101
+ Every session's driving is saved as a flow at teardown when it declared a consequence. These flows:
102
+
103
+ - are named from the `intent` you pass to `reticle_act_and_wait` (`drive-close-an-issue`), and carry it as the flow's `intent`;
104
+ - merge: another drive that starts on the same page and takes the same steps updates the existing file instead of adding a copy. The existing flow keeps its name and everything it already says; the new drive only fills gaps. Flows you named yourself are never merged;
105
+ - record, on every step, the `page` it ran on and the `endPage` it led to;
106
+ - declare `requires` (the start page) and `ensures` (the end page) as route claims, so `canFollow` can tell which flows chain. Replay treats a required start page as met by navigating there;
107
+ - carry `author: { agent, person }`: the MCP client that drove it and the email of whoever ran `reticle login` on that machine. Either is left out when it is not known.
108
+
90
109
  ## Delete a flow
91
110
 
92
111
  ```jsonc
@@ -105,7 +124,7 @@ A renamed or obsolete flow otherwise lingers in `reticle_flow {action:"list"}` a
105
124
  - `drift`: an anchor missed (a testid was renamed, or a signal never fired). The result is **legible**, never a blind failure: `{ step, anchor, drift: { reasonKind: "testid_not_found", nearest: "send-message" } }`. (This is the "whose fault is it" principle.)
106
125
  - `error`: the flow file is missing/invalid, or a resolved action failed. Runtime failures include the failed step and a top-level error envelope.
107
126
 
108
- A testid-_preserving_ refactor (you moved markup but kept the testids) still replays green. A step whose element has **no testid** is anchored on its component + source location (`{ kind: "component", component, source: { file, line } }`), an auto-derived stable anchor, so a flow records cleanly with zero hand-added testids and replay re-resolves it via `reticle_query by:'component'`.
127
+ A testid-_preserving_ refactor (you moved markup but kept the testids) still replays green. A step whose element has **no testid** is anchored on its component + source location (`{ kind: "component", component, source: { file, line } }`), an auto-derived stable anchor, so a flow records cleanly with zero hand-added testids and replay re-resolves it via `reticle_look { action: "find" } by:'component'`.
109
128
 
110
129
  ### The decision envelope: what to do next, not just pass/fail
111
130
 
@@ -175,7 +194,7 @@ With `apply: false` the flow file is **never modified**; you get the proposed di
175
194
  | `reticle_flow {action:"list"}` | `{}` | flows on disk |
176
195
  | `reticle_flow {action:"load"}` | `{ flowName }` | the flow JSON |
177
196
  | `reticle_flow {action:"delete"}` | `{ flowName }` | `{ deleted: true }`, or `{ error, code }` (`not_found` when no such flow) |
178
- | `reticle_flow_replay` | `{ flowName }` | `{ status, steps, decision? }` (decision on drift/fail) |
197
+ | `reticle_flow_replay` | `{ flowName, from?, sweep? }` | `{ status, steps, decision? }` (decision on drift/fail); `from` resumes at a step |
179
198
  | `reticle_verify {action:"flows"}` | `{ names?, sessionId? }` | suite verdict `{ status, passed, failed, failures[] }` |
180
199
  | `reticle_verify { action: "heal" }` | `{ flowName, apply? }` | propose / apply nearest-match rebind |
181
200
  | `reticle_annotate` | `{ kind, … }` | compile a structured annotation into the flow |
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  title: 'Frameworks: proven, wired, and the difference'
3
3
  sidebarTitle: Frameworks
4
- description: Which frameworks Reticle supports, which are wired but unverified, and the exact wiring for each one. React, Next.js, Remix, Astro, SvelteKit, TanStack Start, Nuxt, Vue and plain HTML.
4
+ description: Which frameworks Reticle supports, which are wired but unverified, and the exact wiring for each one. React, Next.js, Remix, Astro, SvelteKit, TanStack Start, Nuxt, Vue, Angular and plain HTML.
5
5
  icon: layer-group
6
6
  ---
7
7
 
8
- **React + Vite, Next.js, Remix and Astro are fully supported, each with an example app driven to a verdict in CI. Astro, SvelteKit, Nuxt, React Router framework mode, Create React App and Vue have their INSTALL gated: the install gate scaffolds each from scratch on every change and requires a real session, but no gate drives them to a verdict. TanStack Start, Preact and plain HTML are wired and documented but not gated at all, so treat them as plausible rather than proven.** The exact wiring for each is below.
8
+ **React + Vite, Next.js, Remix and Astro are fully supported, each with an example app driven to a verdict in CI. Astro, SvelteKit, Nuxt, React Router framework mode, Create React App, Vue and Angular have their INSTALL gated: the install gate scaffolds each from scratch on every change and requires a real session, but no gate drives them to a verdict. TanStack Start, Preact and plain HTML are wired by `init` and documented but not gated at all, so treat them as plausible rather than proven.** The exact wiring for each is below.
9
9
 
10
10
  Reticle is a verification layer that embeds a dev-only SDK in your running web app so an AI coding agent can prove a change works. Its core is framework-neutral: DOM, network, console, routing, animations and source mapping work anywhere JavaScript runs.
11
11
 
@@ -32,9 +32,10 @@ Two columns say different things and it is worth keeping them apart. **CI gate**
32
32
  | **Nuxt** | yes | no | install only | unproven | Install gated, drive unverified |
33
33
  | **React Router (framework mode)** | yes | no | install only | yes | Install gated, drive unverified |
34
34
  | **Create React App** | yes | no | install only | unproven | Install gated, drive unverified |
35
+ | **Angular 17+** | yes | no | install only | no | Install gated, drive unverified |
35
36
  | **Plain HTML** | yes | no | no | no | Wired, unverified |
36
37
  | **Vue (non-Nuxt)** | as Vite | no | install only | no | Install gated, drive unverified |
37
- | **Preact** | as Vite | no | no | unproven | Sensor, React adapter via `preact/compat` |
38
+ | **Preact** | as Vite | no | no | transform-tested; drive unproven | Sensor, React adapter via `preact/compat` |
38
39
  | **Server-rendered (Django, Flask, FastAPI, Streamlit, Rails)** | yes | no | no | no | Wired, unverified |
39
40
 
40
41
  <Warning>
@@ -86,9 +87,9 @@ module.exports = withReticle({/* your config */});
86
87
 
87
88
  ## Remix and Astro
88
89
 
89
- Remix uses the Vite plugin with `inject: false`: Remix SSRs its own HTML, so the plugin's connect-injection does not fire, but the stamping half still puts `file:line` on a verdict.
90
+ Remix v2 on Vite is its own framework to `init`, not plain Vite. Remix SSRs its own HTML, so the plugin's `index.html` injection never reaches the page and the connect comes from `app/entry.client.tsx` instead, the same way as React Router framework mode. `init` adds the dev-only connect line to that entry, or writes the file when the app has none: `app/entry.client.tsx` only exists after `remix reveal`, so the written file is Remix's own default entry (`RemixBrowser`, `hydrateRoot`) with the one line added. The Vite plugin, `reticle()` in the `vite.config` that carries `vitePlugin`, stamps `file:line` and serves the connect module the entry imports. The classic Remix compiler (no `vite.config`) gets a manual step rather than a green plan, because there is no plugin to serve that module.
90
91
 
91
- Astro is the same on the plugin (wire it with `inject: false` if you want source pointers on JSX islands) and uses a different channel for the pairing token. `vite.define` does not reach Astro's client pipeline on 7.2+, so the identifier stays literal and the bridge refuses the dial. `init` reads the token in frontmatter and puts it on a `<meta name="reticle-pairing-token">`. Reading it per request also means a token written after the dev server started is picked up on the next load, with no restart.
92
+ Astro uses the Vite plugin (wire it with `inject: false` if you want source pointers on JSX islands) and uses a different channel for the pairing token. `vite.define` does not reach Astro's client pipeline on 7.2+, so the identifier stays literal and the bridge refuses the dial. `init` reads the token in frontmatter and puts it on a `<meta name="reticle-pairing-token">`. Reading it per request also means a token written after the dev server started is picked up on the next load, with no restart.
92
93
 
93
94
  Use a processed page `<script>` with a static SDK import. Vite owns that module graph from the first transform:
94
95
 
@@ -155,7 +156,7 @@ useEffect(() => {
155
156
  }, []);
156
157
  ```
157
158
 
158
- `init` detects Start and prints this recipe. There is no example app and no CI gate, so treat a connected session as the proof, not the plan.
159
+ `init` writes this for you: a `src/reticle-connect.tsx` client effect that connects, declares `__RETICLE_TOKEN__` so `tsc` passes, and loads `src/reticle-dev.ts`, plus `<ReticleConnect />` rendered before `<Scripts />` in `src/routes/__root.tsx`. When the root route is not a shape it can patch, it prints a recipe naming the real parts (`shellComponent`, `<Scripts />`) instead. There is no example app and no CI gate, so treat a connected session as the proof, not the plan.
159
160
 
160
161
  ## Nuxt
161
162
 
@@ -217,6 +218,10 @@ Detected as its own UI library, because a Preact app using `preact/compat` alias
217
218
 
218
219
  The sensor works. If you use `preact/compat`, the React adapter can attach through the alias, but that path is not gated.
219
220
 
221
+ **The source-pointer transform works on Preact code, and that is measured rather than assumed.** The stamper is JSX-syntactic: it tags a host element by its tag name and reads nothing about React, so a Preact `.jsx` or `.tsx` file is stamped exactly as a React one is. `class` instead of `className`, a `@jsxImportSource preact` pragma and a `@preact/signals` value in the tree all leave it unmoved. There are tests for each of those in `adapters/build/babel-plugin`. They prove the Babel transform, not the `@preact/preset-vite` pipeline end to end: nobody has yet driven a Preact app and seen a verdict name a `file:line`, so the drive stays unproven until someone does.
222
+
223
+ What is still missing on Preact is the enrichment above the DOM: `reticle_inspect` returns no `component`/`componentStack`, `@preact/signals` has no `reticle_state` adapter, and the render meter counts React commits only. Tracked in [#129](https://github.com/reticlehq/reticle/issues/129).
224
+
220
225
  ## Create React App and plain bundlers
221
226
 
222
227
  No config file to patch, so `connect()` goes in your entry module.
@@ -231,14 +236,28 @@ The same shape works for webpack, Parcel, and the Vue and Svelte CLIs.
231
236
 
232
237
  ## Plain HTML
233
238
 
239
+ A directory with an `index.html` and no `package.json` is wired by `init` itself: it writes the connect snippet into `index.html`, before `</body>`, writes `.reticle.json`, and hands over to setup. The block is marked, so a re-run finds it and never writes a second one, and a re-run with a new `--port` rewrites the port inside it. It loads the SDK from a CDN and runs only when the page is opened on `localhost`, `127.0.0.1` or `[::1]`: a static file has no development build to strip it, so the host is the only line between the snippet and a published page. The page carries no pairing token: `init` writes it into `reticle.local.js` beside the page, which the snippet imports, and adds that file to `.gitignore`. Keep it out of anything you publish, and let each teammate run `reticle init` for their own. Several pages and no `index.html` is a choice `init` does not make for you: it prints the snippet, filled in with your project id and token, for you to paste:
240
+
234
241
  ```html
235
242
  <script type="module">
236
- import { reticle } from '/node_modules/@reticlehq/browser/dist/index.js';
237
- reticle.connect();
243
+ import { reticle } from 'https://cdn.jsdelivr.net/npm/@reticlehq/browser@<version>/+esm';
244
+ reticle.connect({ projectId: '<your-project-id>', token: '<pairing token>' });
238
245
  </script>
239
246
  ```
240
247
 
241
- Keep it out of your production template. Reticle self-disables when the build reports `NODE_ENV=production`, but a second lock on a door leading to your users is a reasonable number.
248
+ A bare `/node_modules/...` import does not work here: the SDK's own `dist` imports bare specifiers, which a browser cannot resolve without a bundler. A page with a `package.json` and a bundler takes the entry-module path above instead.
249
+
250
+ ## Angular
251
+
252
+ Angular 17 and later, on the Vite-based `@angular/build` dev server. `init` installs only `@reticlehq/browser`, the framework-neutral sensor, and makes three edits:
253
+
254
+ - **The browser entry** (`src/main.ts`, or whatever `angular.json` names) gets a connect guarded on Angular's `ngDevMode` global (`typeof ngDevMode === 'undefined' || ngDevMode`), through a dynamic `import('@reticlehq/browser')` appended after the bootstrap. A production `ng build` defines `ngDevMode` as `false`, so the build drops the block and the chunk with it. The output carries no Reticle code at all, and the app never waits on Reticle. `isDevMode()` is not used because the build cannot fold a function call; an entry wired that way by an older `init` is rewritten in place on the next run.
255
+ - **`reticle.proxy.mjs`**, beside `angular.json`, answers one same-origin route, `/__reticle/pairing-token`, with this machine's pairing token, read from `~/.reticle/pairing-token` at request time. It refuses a request that did not arrive over loopback or came from another origin.
256
+ - **`angular.json`** gets the serve target's `proxyConfig` pointed at that file. `ng serve` loads it; `ng build` never reads a proxy config, so a production bundle has no route to the token and the token is never written into the project.
257
+
258
+ The token takes that route because the Angular CLI has no config file that runs code: a literal in `angular.json` commits a per-machine secret, and a gitignored module imported from `main.ts` is bundled into `ng build` whether or not anything can reach it. An existing `proxyConfig` is yours and is never replaced: the step goes manual and names the entry to add to it. A workspace with several served applications is also manual, because which one is the app is not `init`'s to guess.
259
+
260
+ You get DOM, network, console, routing and state tools. You do not get component identity (that is the React adapter) or a `source` field (the stamper covers JSX and Svelte). The install gate scaffolds an Angular app (no SSR) on every change and requires a session; SSR was proven by hand, and nothing drives an Angular app to a verdict.
242
261
 
243
262
  ## Server-rendered apps (Django, Flask, FastAPI, Rails)
244
263
 
@@ -327,7 +346,7 @@ Electron and Tauri invert the direction: your app dials the daemon, and there is
327
346
 
328
347
  ## Not supported
329
348
 
330
- Angular and Solid appear in dependency detection but have no wiring, no adapter and no gate. The sensor may work if you call `connect()` yourself. Nothing proves it, so we are not going to list them as supported.
349
+ Solid appears in dependency detection but has no wiring, no adapter and no gate. The sensor may work if you call `connect()` yourself. Nothing proves it, so we are not going to list them as supported.
331
350
 
332
351
  ## Frequently asked questions
333
352
 
@@ -4,7 +4,7 @@ description: 'Zero to your agent verifying your real app, step by step, with wor
4
4
  icon: rocket
5
5
  ---
6
6
 
7
- **To get started with Reticle: run the installer, open your coding agent, then run `npx @reticlehq/server init` in your project root.** Getting started is three stages. **Installation** is one command in a terminal, `curl -fsSL https://raw.githubusercontent.com/reticlehq/reticle/main/install/install.sh | sh` (or `irm https://raw.githubusercontent.com/reticlehq/reticle/main/install/install.ps1 | iex` on Windows): it puts the CLI on the machine and registers the MCP server with every agent it can reach, so the tools are there the next time you open your agent. **Onboarding** is `init`: it wires your project, starts your dev server, opens the app and waits for it to connect. **The first run** is what proves anything: `reticle_verify { action: "explore", persona: "<the journey worth proving>" }`, or from a terminal `npx @reticlehq/server verify <url> --explore --persona "<the journey worth proving>"`. A connected app is not a verified one. Reticle is a verification layer that embeds a dev-only SDK in your running web app so an AI coding agent can prove a change works instead of guessing. It needs Node 20 or newer, an app you run locally, and an agent that speaks MCP.
7
+ **To get started with Reticle Cloud: run the installer, then `reticle connect --project "My App"` in your app directory before opening your coding agent.** The installer puts the CLI on the machine and registers its MCP tools with supported agents. `connect` wires your app, checks that a browser session connected, opens sign-in approval if needed, links the named cloud project, and syncs existing history. The first run is what proves anything: ask your agent to verify one journey in your app, or use `reticle_verify { action: "explore", persona: "<the journey worth proving>" }`. A connected app is not a verified one. For local-only verification without an account, use `reticle init` instead. You need Node 20.11 or newer, an app you run locally, and an agent that speaks MCP.
8
8
 
9
9
  > **Looking for the fast path?** [Quickstart](/quickstart) gets you to a real verdict in five minutes, and every response on it was captured live. [Agentic install](/install-agentic) and [Manual install](/install-manual) cover setup in detail, per agent and per framework.
10
10
  >
@@ -58,7 +58,17 @@ Everything is **dev-only** and **localhost-only**. It's tree-shaken out of produ
58
58
 
59
59
  ---
60
60
 
61
- ## Fastest path: `reticle init`
61
+ ## Cloud setup: `reticle connect`
62
+
63
+ After the one-time [installer](/quickstart), run this in your app directory:
64
+
65
+ ```bash
66
+ npx @reticlehq/server connect --project "My App"
67
+ ```
68
+
69
+ Approve the short browser code if Reticle asks you to sign in. The command then links this folder and sends any history already on the machine. Open or restart your coding agent and ask it to verify one real journey. For a new project, the dashboard has no result until that first run is recorded.
70
+
71
+ ## Local-only path: `reticle init`
62
72
 
63
73
  From your project root:
64
74
 
@@ -78,7 +88,7 @@ The bridge + MCP server is a single process that serves all your projects, so it
78
88
 
79
89
  Re-running is safe: already-registered and already-patched steps are skipped, and on a wired project it goes straight to proving the app still works. Preview without writing via `npx @reticlehq/server init --dry-run`.
80
90
 
81
- `init` does not stop at writing files. It starts your dev server (restarting one whose bundle predates the config edit), opens the app, and waits for a session to connect from inside it. That connection is the whole proof that onboarding worked. It exits non-zero if nothing connected and prints what is left to do. It does NOT drive. Then prove a flow. That is the FIRST RUN, and it is a separate call: `reticle_verify { action: "explore", persona: "<who does what>" }`. It drives with a model inside the daemon and records what it drove, so later checks replay it with no model in the loop.
91
+ `init` does not stop at writing files. It starts your dev server, opens the app, and waits for a session to connect from inside it. That connection is the whole proof that onboarding worked. If a dev server was already running without Reticle, restart it once: it read the build config before `init` edited it. It exits non-zero if nothing connected and prints what is left to do. It does NOT drive. Then prove a flow. That is the FIRST RUN, and it is a separate call: `reticle_verify { action: "explore", persona: "<who does what>" }`. It drives with a model inside the daemon and records what it drove, so later checks replay it with no model in the loop.
82
92
 
83
93
  These carry what the command cannot work out for itself:
84
94
 
@@ -90,7 +100,7 @@ These carry what the command cannot work out for itself:
90
100
 
91
101
  The rest are dials: `--license <key>` (writes it to `.env` and keeps `.env` out of git), `--json` (one object for an agent to read), `--files-only` (write, register, pre-approve, and stop, which is what `init` did before it learned to boot the app, and what an existing install re-runs to pick up new wiring), `--relaunch` (prints the command that restarts the conversation you are in, with the tools loaded: the restart step most installs stall on, and it composes with `--files-only`), `--no-open`, `--dry-run`, `--port N`, `--no-mcp`, `--no-install`.
92
102
 
93
- Then restart your dev server and skip to [Step 4](#step-4-run-it-and-verify-the-connection). The manual steps below explain what `init` sets up, if you prefer to wire it yourself.
103
+ If you ran `--files-only` with a dev server already running, restart it, then skip to [Step 4](#step-4-run-it-and-verify-the-connection). The manual steps below explain what `init` sets up, if you prefer to wire it yourself.
94
104
 
95
105
  ---
96
106
 
@@ -464,7 +474,7 @@ Everything below comes from the `@reticlehq/react` kit plus your framework's bui
464
474
 
465
475
  **You do not get** component identity. `@reticlehq/react` walks the fiber tree to answer "which component rendered this element"; there is no Svelte equivalent, so snapshots carry the file and line but no component name. Stamping targets Svelte 5's compiler AST and also accepts Svelte 4's; `.svelte.ts` runes modules are code rather than markup and are not stamped.
466
476
 
467
- **It is still unverified.** There is no SvelteKit app in `apps/` and no CI gate for one, so nothing would tell us when this breaks; `reticle init` says so out loud in its plan. React, Next.js, Remix and Astro each have an app and a gate. Treat SvelteKit as wired and plausible, not as supported.
477
+ **It is install-gated, drive-unverified.** The install gate scaffolds a SvelteKit app from scratch, runs `init`, boots it and waits for a session, so the setup is proven. There is no SvelteKit example app that CI drives to a verdict, so the drive is not. [Frameworks](/frameworks) has the full status table.
468
478
 
469
479
  **Vue is install-gated, drive-unverified.** `init` detects a Vue app (as a Vite app with Vue as the UI library) and the install gate scaffolds one from scratch, runs `init`, boots it and waits for a session, so the setup is proven. What is not proven is the drive: there is no Vue example app in CI. `piniaStore` reads a Pinia store, and everything the framework-neutral core provides works. What you do not get is a `source` field, because source stamping covers JSX and Svelte components and a `.vue` single-file component is neither. See [Frameworks](/frameworks) for the full status table.
470
480
 
@@ -8,7 +8,7 @@ Most clients reach Reticle over stdio: they spawn `reticle mcp`, and the tools a
8
8
 
9
9
  The daemon also speaks MCP over HTTP, on the same port as everything else. Nothing has to reload for a client to reach it, so this is the transport for scripted runs, CI, a language with no MCP client library, and any editor that will not pick up tools mid-session.
10
10
 
11
- It is the same tool surface. Not a subset, not a simplified one. `reticle_snapshot`, `reticle_act_and_wait`, `reticle_assert` and the rest behave exactly as they do over stdio, because they are the same server behind a different transport.
11
+ It is the same tool surface. Not a subset, not a simplified one. `reticle_look { action: "page" }`, `reticle_act_and_wait`, `reticle_assert` and the rest behave exactly as they do over stdio, because they are the same server behind a different transport.
12
12
 
13
13
  ## The two endpoints
14
14
 
@@ -136,7 +136,10 @@ async function start() {
136
136
  const listed = await send('tools/list', {});
137
137
  console.log(listed.result.tools.map((t) => t.name).join(', '));
138
138
 
139
- const snapshot = await send('tools/call', { name: 'reticle_snapshot', arguments: {} });
139
+ const snapshot = await send('tools/call', {
140
+ name: 'reticle_look',
141
+ arguments: { action: 'page' },
142
+ });
140
143
  console.log(snapshot.result.content[0].text);
141
144
  }
142
145
  ```
@@ -159,6 +162,6 @@ The `202` comes back here; the tool list appears in the first shell.
159
162
 
160
163
  ## What this does not change
161
164
 
162
- The transport carries the tools. It does not replace the rest of the setup. The app still has to be instrumented and connected, and `reticle_sessions` is still what tells you whether a session is there to drive. A verdict reached over HTTP is a verdict reached the usual way: only `verified: "yes"` is a pass.
165
+ The transport carries the tools. It does not replace the rest of the setup. The app still has to be instrumented and connected, and `reticle_session { action: "list" }` is still what tells you whether a session is there to drive. A verdict reached over HTTP is a verdict reached the usual way: only `verified: "yes"` is a pass.
163
166
 
164
167
  The endpoints, the handshake, the three status codes and the `202`-then-SSE shape are pinned by `server/src/mcp-http-transport.test.ts`, which drives them over raw HTTP with no client library, so the contract this page describes fails the build if it changes.
@@ -17,7 +17,7 @@ The panel header shows a running **✓N ✗M** tally: the verdict score, live. I
17
17
  MCP is **pull-based**: the agent only receives data when it calls a tool. Reticle can't freeze the model mid-thought, but it **intercepts the agent's next tool call**, which is exactly when the agent would touch the page. That makes steering reliable:
18
18
 
19
19
  - **Nudge** (a message, no pause). Your text is queued and rides back on the agent's **next** tool result as `guidance` (delivered once). The agent reads it and adjusts.
20
- - **Pause.** The agent's next `reticle_act` / `reticle_act_and_wait` / `reticle_act_sequence` **refuses to execute** and returns `{ paused: true, guidance: ["<your message>"], hint: "…" }`. The page is untouched until resume. _Read-only tools (snapshot/query/observe) still work_, so the agent can look while paused.
20
+ - **Pause.** The agent's next `reticle_act` (single or `steps`) / `reticle_act_and_wait` **refuses to execute** and returns `{ paused: true, guidance: ["<your message>"], hint: "…" }`. The page is untouched until resume. _Read-only tools (snapshot/query/observe) still work_, so the agent can look while paused.
21
21
  - **End.** The session is over; the panel shows "Session ended" and clears.
22
22
 
23
23
  ## From the panel (the human)
@@ -35,7 +35,7 @@ The floating panel (bottom-center, `present: true`) gives you:
35
35
  You don't have to describe a bug in prose. **Point at it.** The flag captures the element's re-resolvable anchor _and_ the source `file:line` (when the framework stamped one), so the agent fixes the exact element and code, not a guess. The loop:
36
36
 
37
37
  1. **You** flag the element and type the problem → Reticle emits a `HUMAN_MARK`.
38
- 2. **The agent** drains it with `reticle_session {action:"review"}`, getting note + element label + `source: { file, line }` + a ready-to-act `fix` hint. `reticle_sessions` also reports `pendingMarks` so the agent notices flags.
38
+ 2. **The agent** drains it with `reticle_session {action:"review"}`, getting note + element label + `source: { file, line }` + a ready-to-act `fix` hint. `reticle_session { action: "list" }` also reports `pendingMarks` so the agent notices flags.
39
39
  3. **The agent** opens the file, fixes it, and calls `reticle_session {action:"review"}({ resolve: "m1" })`.
40
40
  4. **You** see **"✓ fixed: \<your note\>"** land in the panel. Flag → fix → confirmation.
41
41
 
package/docs/index.mdx CHANGED
@@ -73,13 +73,16 @@ Afterwards, use it whenever you change any user-facing behaviour.
73
73
  The four files it writes, the marks it prints, and how to read its report.
74
74
  </Card>
75
75
 
76
- <CardGroup cols={2}>
76
+ <CardGroup cols={3}>
77
77
  <Card title="Quickstart" icon="rocket" href="/quickstart">
78
78
  Install, connect your agent, and get a real verdict. Five minutes.
79
79
  </Card>
80
80
  <Card title="Why Reticle" icon="lightbulb" href="/why-reticle">
81
81
  The false-green problem, and the evidence that this fixes it.
82
82
  </Card>
83
+ <Card title="Use cases" icon="list-check" href="/use-cases">
84
+ Agent-built changes, security behaviour, accessibility, performance, SEO basics and personas.
85
+ </Card>
83
86
  </CardGroup>
84
87
 
85
88
  <CardGroup cols={3}>
@@ -127,18 +130,25 @@ Reticle clicks, watches, and reports what actually happened. A verdict of `unkno
127
130
  single step.
128
131
  </Card>
129
132
  <Card title="Yours, locally" icon="lock">
130
- Dev-only, localhost-only, offline. Your app's data never leaves the machine.
133
+ Dev-only and localhost-only. Your app's data stays on your machine; anonymous usage counts are
134
+ sent unless you [turn them off](/telemetry).
131
135
  </Card>
132
136
  </CardGroup>
133
137
 
138
+ ## What people use it for
139
+
140
+ Beyond proving an agent's change, people use Reticle to check security behaviour (access control by role, calls that must never fire, CSP violations), accessibility and UX (roles, names, focus, keyboard), performance (request counts, long tasks, layout shift), SEO basics (titles, headings, links, redirects), and to drive the app as different users. [Use cases](/use-cases) has what each one checks and a call to run.
141
+
142
+ It pairs well with a visual testing tool for pixel diffs, Playwright for sites you don't own, axe for full WCAG audits and a security scanner for vulnerability discovery.
143
+
134
144
  ## The evidence
135
145
 
136
- We publish the losses too. Reticle is not the right tool for driving a site you don't own, and it does not see pixels.
146
+ Every number is measured by a committed harness, including the ties.
137
147
 
138
148
  ![One honest test, two apps: Reticle has the highest Verification Efficiency on the controlled app and the lowest observation cost on the real dashboard](/images/bench-two-apps.png)
139
149
 
140
150
  <Card title="Read the full benchmark" icon="chart-column" href="/benchmarks">
141
- How every number was measured, what it means, and where Reticle comes second.
151
+ How every number was measured, what it means, and where the tools tie.
142
152
  </Card>
143
153
 
144
154
  ## Built for agents, readable by humans
@@ -151,7 +161,7 @@ Reticle is used by a coding agent far more often than by a person, so these docs
151
161
 
152
162
  ## Build with us
153
163
 
154
- Reticle is early, and the sharp edges get filed down fastest when someone tells us where they are.
164
+ The roadmap moves on what people actually hit, so tell us.
155
165
 
156
166
  <CardGroup cols={2}>
157
167
  <Card title="Join the Discord" icon="discord" href="https://discord.gg/BwAbzv9ZRz">
@@ -163,4 +173,4 @@ Reticle is early, and the sharp edges get filed down fastest when someone tells
163
173
  </Card>
164
174
  </CardGroup>
165
175
 
166
- Found a bug, or something you wished existed? `reticle_feedback` sends it in one call, from wherever you are. If Reticle could not tell you what happened, that is our defect and we want to know.
176
+ Found a bug, or something you wished existed? `reticle_session { action: "feedback" }` sends it in one call from your agent, and `npx @reticlehq/server feedback "…"` from a terminal. If Reticle could not tell you what happened, that is our defect and we want to know.
@@ -131,7 +131,7 @@ That `ℹ` line is the one people skim past, so here it is in full:
131
131
  ```
132
132
  [ℹ] AGENT: finish the capabilities file → src/reticle-dev.ts
133
133
  src/reticle-dev.ts was written but registers nothing, so `hasCapabilities` stays false
134
- and reticle_state has nothing to read.
134
+ and reticle_look { action: "state" } has nothing to read.
135
135
  ```
136
136
 
137
137
  `init` wrote the file, but a brand-new Vite template has no testids and no state library, so there was nothing to register. Reticle tells you this instead of counting it as a win. See [Instrument your app](/instrumentation) for what to put in it.
@@ -248,7 +248,7 @@ Three of these are answers only the caller has. `init` reads your repository; it
248
248
  | `--env KEY=VALUE` | What the app needs to reach a usable state: the key from `.env.example`, the mock backend, the variable that skips an auth wall. Repeatable, and a value may contain spaces and equals signs. Without it a real app sits on a login screen and there is no flow to drive. |
249
249
  | `--app <dir>` | Monorepos. `init` can find the apps that are servable; only your request says which one you are working in. |
250
250
  | `--license <key>` | Writes `RETICLE_LICENSE_KEY` to `.env` and keeps `.env` out of git. Do not do those two by hand. |
251
- | `--json` | One object on stdout: what was wired, what connected, the verdict, and an `agentTodo` array. For agents. |
251
+ | `--json` | One result object on stdout; progress and package-manager output go to stderr. Default init reports runtime setup and connection. `--files-only` and `--dry-run` report the wiring result. Init does not drive a flow or produce a verification verdict. |
252
252
  | `--files-only` | Write the files, register the MCP server with every agent on the machine, pre-approve Reticle's tools, and stop: no dev server, no drive. What `init` did before it learned to boot the app, and the upgrade path for a machine that installed Reticle on an earlier version. |
253
253
  | `--no-open` | Do not open a browser: CI, a container, an SSH session, or a tab you already have. |
254
254
  | `--dry-run` | See the plan before anything is written. |
@@ -175,7 +175,7 @@ Screenshots and headless mode need the Rust crate, which is versioned independen
175
175
 
176
176
  ```toml
177
177
  [dependencies]
178
- reticle-tauri = "3.3"
178
+ reticle-tauri = "3.5"
179
179
  ```
180
180
 
181
181
  ```rust
@@ -22,7 +22,7 @@ The basics in [Getting Started](getting-started.md) work with **zero app changes
22
22
 
23
23
  Adoption is **free → cheap → targeted**. You don't instrument everything; you reuse what you have, then add the handful of facts the DOM can't express.
24
24
 
25
- **1. Reuse your existing `data-testid` (free).** If you already test with Playwright or Cypress, your testids work in Reticle unchanged; `reticle_query({ by: 'testid', value: 'checkout' })` matches them exactly. No new markup, no new code.
25
+ **1. Reuse your existing `data-testid` (free).** If you already test with Playwright or Cypress, your testids work in Reticle unchanged; `reticle_look({ action: "find", by: 'testid', value: 'checkout' })` matches them exactly. No new markup, no new code.
26
26
 
27
27
  **2. Advertise the surface from your existing constants (cheap).** You already keep a `TestIds` constant object for your E2E suite. Pass it straight in. Now `reticle_capabilities()` tells a fresh agent your whole surface without reading source.
28
28
 
@@ -107,7 +107,7 @@ If `mutate` throws, the mutation never happened, so **the signal is not emitted
107
107
 
108
108
  > **The documented exception:** genuinely view-level signals (render or async completions like `diff:shown` or `caption:generated`, which aren't store state) legitimately stay in your components. Only commit-point signals belong in the store layer.
109
109
 
110
- Pair this with store registration so the agent can _read_ state instead of you emitting a signal per fact: `registerStore('workspace', useWorkspace)`, then `reticle_state({ store: 'workspace' })`.
110
+ Pair this with store registration so the agent can _read_ state instead of you emitting a signal per fact: `registerStore('workspace', useWorkspace)`, then `reticle_look({ action: "state", store: 'workspace' })`.
111
111
 
112
112
  ## 4. Self-registering domains (`registerReticleDomain`)
113
113
 
@@ -164,7 +164,7 @@ Or turn it on with the shipped preset: `plugin.configs.recommended`, which sets
164
164
 
165
165
  Reticle observes and drives a tab through the in-page SDK plus (optionally) CDP. It **cannot bring to front or recover a browser tab the OS won't let it script**: a backgrounded tab, say, or a non-default browser (Dia, etc.) reporting `hidden:true` / `throttled:true`.
166
166
 
167
- When that happens, `reticle_sessions` and every act/assert result carry a `session.recommendation` saying so. The escape hatch is **`reticle drive <url>`** (add `--headed` to watch). Reticle launches and owns a guaranteed-scriptable browser. See [usage §18](usage.md#18-real-input-mode-native-hover--drag) for the full note.
167
+ When that happens, `reticle_session { action: "list" }` and every act/assert result carry a `session.recommendation` saying so. The escape hatch is **`reticle drive <url>`** (add `--headed` to watch). Reticle launches and owns a guaranteed-scriptable browser. See [usage §18](usage.md#18-real-input-mode-native-hover--drag) for the full note.
168
168
 
169
169
  ## Checklist
170
170
 
@@ -45,7 +45,7 @@ This is the design target, and it needs no special setup:
45
45
  - Flows can't bleed into each other: contexts are isolated and every session is scoped by `projectId`.
46
46
  - A single crashed page is reclaimed on its own, and if an agent crashes or hangs its lease stops being touched and the **lease reaper** reclaims the context after a TTL, freeing the slot. One dead agent never starves the others.
47
47
 
48
- `reticle_sessions` lists everything with `projectId` (group by app) and `leased` (pool context vs a human tab), so an orchestrator can see the whole fleet at a glance.
48
+ `reticle_session { action: "list" }` lists everything with `projectId` (group by app) and `leased` (pool context vs a human tab), so an orchestrator can see the whole fleet at a glance.
49
49
 
50
50
  ## Knobs
51
51
 
@@ -76,4 +76,4 @@ module.exports = {
76
76
 
77
77
  ## Why an attribute rather than a runtime lookup
78
78
 
79
- The stamp survives minification of everything else, costs nothing at runtime, and is readable from the DOM node itself. That is what lets [`reticle_inspect`](/tools/inspect) answer with a file and a line rather than a component name and a shrug.
79
+ The stamp survives minification of everything else, costs nothing at runtime, and is readable from the DOM node itself. That is what lets [`reticle_look { action: "element" }`](/tools/inspect) answer with a file and a line rather than a component name and a shrug.
@@ -12,7 +12,7 @@ icon: window
12
12
 
13
13
  This is what makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel. It never imports a Node API; that boundary is enforced by the dependency graph.
14
14
 
15
- [`reticle_query`](/tools/query) speaks role, label and testid instead of CSS selectors through its own local accessibility engine: one engine computes both what elements are called and what they match by, so a reported identity always round-trips.
15
+ [`reticle_look { action: "find" }`](/tools/query) speaks role, label and testid instead of CSS selectors through its own local accessibility engine: one engine computes both what elements are called and what they match by, so a reported identity always round-trips.
16
16
 
17
17
  ## When you need it directly
18
18
 
@@ -90,7 +90,7 @@ Also exported: `getCapabilities()`, `hasCapabilities()`, `setCapabilitiesListene
90
90
 
91
91
  ## State adapters
92
92
 
93
- Register a store and its contents become readable through [`reticle_state`](/tools/state).
93
+ Register a store and its contents become readable through [`reticle_look { action: "state" }`](/tools/state).
94
94
 
95
95
  `registerStore`, `unregisterStore`, `storeNames`, `readStores`, `readStoresWithTruncation`.
96
96
 
@@ -44,10 +44,13 @@ Returns the config unchanged when `NODE_ENV === 'production'`. Otherwise it merg
44
44
  | --- | --- |
45
45
  | `webpack` | Pushes an `enforce: 'pre'` rule for `/\.(t\|j)sx$/`, excluding `node_modules`, using this package's loader. Your own `webpack` function still runs afterwards |
46
46
  | `turbopack` | On Next 15.3 and above only: rules for `*.tsx` and `*.jsx` pointing at `@reticlehq/next/loader` |
47
+ | `env.NEXT_PUBLIC_RETICLE_URL` | The bridge URL: a valid `RETICLE_PORT` override, otherwise project daemon discovery and `.reticle.json` |
47
48
  | `env.NEXT_PUBLIC_RETICLE_TOKEN` | The pairing token, when one exists on disk |
48
49
  | `env.NEXT_PUBLIC_RETICLE_ROOT` | `process.cwd()`, so source paths come back repository-relative |
49
50
  | `env.NEXT_PUBLIC_RETICLE_SDK_VERSION` | The version of the app's installed `@reticlehq/react` |
50
51
 
52
+ Daemon discovery reads `RETICLE_STATE_DIR` (default `~/.reticle`). Pairing tokens use `RETICLE_PAIRING_TOKEN_DIR` independently. The generated `ReticleDev` component reads `NEXT_PUBLIC_RETICLE_URL`; restart the dev server after changing these settings.
53
+
51
54
  `readPairingToken()` is also exported at runtime. It is not declared in `index.d.ts`, so TypeScript will not see it.
52
55
 
53
56
  ### `./loader`
@@ -76,7 +76,7 @@ import { useReticleStore } from '@reticlehq/react/store';
76
76
 
77
77
  function Cart() {
78
78
  const [items, setItems] = useState([]);
79
- useReticleStore('cart', items); // now readable via reticle_state
79
+ useReticleStore('cart', items); // now readable via reticle_look { action: "state" }
80
80
  }
81
81
  ```
82
82
 
@@ -87,5 +87,5 @@ function Cart() {
87
87
  React 19 dropped `_debugSource`, so `identify` can name the component but not the file unless something stamps the source at build time. That is [`@reticlehq/babel-plugin`](/packages/babel-plugin), pulled in for you by [`@reticlehq/vite-plugin`](/packages/vite-plugin) or [`@reticlehq/next`](/packages/next).
88
88
 
89
89
  <Card title="Reading component state" icon="layer-group" href="/state-management">
90
- What `reticle_state` can see, and how to widen it.
90
+ What `reticle_look { action: "state" }` can see, and how to widen it.
91
91
  </Card>
@@ -21,7 +21,7 @@ Tauri IPC observation needs nothing on the Rust side. An `invoke('load_todos')`
21
21
 
22
22
  ```toml
23
23
  [dependencies]
24
- reticle-tauri = "3.3"
24
+ reticle-tauri = "3.5"
25
25
  ```
26
26
 
27
27
  Its own dependencies are `tauri = "2"` plus the per-platform capture backends: `objc2` and friends on macOS, `webview2-com` and `windows` on Windows, `webkit2gtk` and `cairo-rs` on Linux and the BSDs.
@@ -31,6 +31,8 @@ export default defineConfig({
31
31
  [`@reticlehq/server`](/packages/server).
32
32
  </Warning>
33
33
 
34
+ Set `RETICLE_PORT` in the dev server or desktop build environment to use the same custom bridge port as the CLI. A valid value overrides discovery and `.reticle.json`; invalid values are ignored.
35
+
34
36
  ## Why it exists
35
37
 
36
38
  `apply: 'serve'` means it never reaches a production build. That is the guarantee that lets you leave it in the config permanently: there is no way to ship Reticle to your users by forgetting to remove a line.
@@ -61,7 +63,7 @@ Plus a dynamic import of your dev module if one exists. It looks for `src/reticl
61
63
  | `token` | `string` | `readPairingToken()`, from `~/.reticle/pairing-token` |
62
64
  | `sourceMapping` | `boolean` | `true`. Set `false` to skip the Babel pass |
63
65
  | `inject` | `boolean` | `true`. Set `false` to call `connect()` yourself |
64
- | `desktop` | `boolean` | `false`. When `true` the plugin also applies to `vite build` and passes `allowInProduction: true`, which is what an Electron or Tauri renderer needs |
66
+ | `desktop` | `boolean` | `false`. When `true` the connect passes `allowInProduction: true`, which an Electron or Tauri renderer needs, and a `vite build` in a non-production mode (`--mode development`) is instrumented too. A production-mode build is stubbed like a web build |
65
67
  | `captureNetworkBodies` | `boolean` | `false`. Also enabled by `VITE_RETICLE_CAPTURE_BODIES=1` |
66
68
  | `onWarn` | `(message: string) => void` | `console.warn` |
67
69
 
@@ -87,7 +89,7 @@ Types: `ReticleVitePluginOptions`, `ReticleVitePlugin`, `ViteDevServerLike`.
87
89
  reticle({ desktop: true });
88
90
  ```
89
91
 
90
- A packaged Electron or Tauri renderer is a production build loaded from a custom protocol, so the default guards would refuse. This one flag drops `apply: 'serve'` and opts the connect call into production.
92
+ A packaged Electron or Tauri renderer is loaded from a custom protocol with no dev server and reports `NODE_ENV=production`, so the default guards would refuse. This flag injects the connect into the entry module and opts it into production. It does NOT instrument a release: a production-mode `vite build` (the default) ships no Reticle code, exactly like a web build. To drive a packaged renderer with no dev server, build it with `vite build --mode development`.
91
93
 
92
94
  <Card title="Desktop wiring in full" icon="display" href="/desktop">
93
95
  Including the Tauri CSP whose failure is completely silent.
package/docs/packages.mdx CHANGED
@@ -38,12 +38,12 @@ You never install `@reticlehq/server`. Your agent runs it with `npx`.
38
38
 
39
39
  The package split is not cosmetic. Each boundary enforces something:
40
40
 
41
- | Package | Runs in | Hard rule |
42
- | --------- | ----------- | ------------------------------------------------------ |
43
- | `core` | both | Depends only on `zod`. Sits at the bottom of the graph |
44
- | `browser` | the browser | Never imports a Node API |
45
- | `server` | Node | Never imports a DOM API |
46
- | `react` | the browser | Optional. Core must work without it |
41
+ | Package | Runs in | Hard rule |
42
+ | --- | --- | --- |
43
+ | `core` | both | Depends only on `zod` and `open-verification`. Sits at the bottom of the graph |
44
+ | `browser` | the browser | Never imports a Node API |
45
+ | `server` | Node | Never imports a DOM API |
46
+ | `react` | the browser | Optional. Core must work without it |
47
47
 
48
48
  Those rules are why the browser SDK cannot accidentally pull in a filesystem call, and why the server cannot accidentally depend on `window`. They are enforced by the dependency graph, not by discipline.
49
49
 
@@ -72,7 +72,7 @@ Those rules are why the browser SDK cannot accidentally pull in a filesystem cal
72
72
 
73
73
  **Why it exists:** this is the part that makes Reticle different from a browser-automation tool. Because it runs _inside_ the app rather than driving it from outside, it can see a state mutation and a fired signal, not just a rendered pixel.
74
74
 
75
- **Depends on:** `@reticlehq/core`. [`reticle_query`](/tools/query) speaks role, label and testid rather than CSS selectors through its own local accessibility engine, so what a snapshot reports is exactly what a query matches.
75
+ **Depends on:** `@reticlehq/core`. [`reticle_look { action: "find" }`](/tools/query) speaks role, label and testid rather than CSS selectors through its own local accessibility engine, so what a snapshot reports is exactly what a query matches.
76
76
 
77
77
  **Install it directly when** you are not on React, or you want `connect()` under your own control:
78
78
 
@@ -147,7 +147,7 @@ export default defineConfig({ plugins: [reticle(), react()] });
147
147
 
148
148
  **Stamps `data-reticle-source="file:line:col"` onto JSX host elements.**
149
149
 
150
- **Why it exists:** React 19 dropped `_debugSource`, which is what source mapping used to rely on. This plugin puts the information back, at build time, in dev only. Without it, `reticle_inspect` can still tell you the component name but not the file.
150
+ **Why it exists:** React 19 dropped `_debugSource`, which is what source mapping used to rely on. This plugin puts the information back, at build time, in dev only. Without it, `reticle_look { action: "element" }` can still tell you the component name but not the file.
151
151
 
152
152
  **Depends on:** nothing. **Peer:** `@babel/core`.
153
153
 
@@ -239,7 +239,7 @@ A lint rule makes the signal layer self-enforcing, which is the only way a conve
239
239
 
240
240
  ```toml
241
241
  [dependencies]
242
- reticle-tauri = "3.3"
242
+ reticle-tauri = "3.5"
243
243
  ```
244
244
 
245
245
  <Warning>