@reticlehq/server 3.7.0 → 3.8.1

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 (578) hide show
  1. package/SKILL.md +59 -68
  2. package/bin/reticle.js +9 -2
  3. package/dist/command/cli/adhoc-suite.js +1 -1
  4. package/dist/command/cli/adhoc-verdict.d.ts +12 -0
  5. package/dist/command/cli/adhoc-verdict.js +62 -4
  6. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  7. package/dist/command/cli/answers/gate-hook-message.js +5 -1
  8. package/dist/command/cli/auth/cloud-session.d.ts +11 -19
  9. package/dist/command/cli/auth/cloud-session.js +23 -0
  10. package/dist/command/cli/auth/device-flow.d.ts +3 -37
  11. package/dist/command/cli/cli-doctor.js +66 -21
  12. package/dist/command/cli/cli-flow-commands.js +53 -11
  13. package/dist/command/cli/cli-kill.d.ts +8 -0
  14. package/dist/command/cli/cli-kill.js +43 -3
  15. package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
  16. package/dist/command/cli/cli-parse-feedback.js +13 -1
  17. package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
  18. package/dist/command/cli/cli-parse-grammar.js +1 -6
  19. package/dist/command/cli/cli-parse-serve.d.ts +24 -0
  20. package/dist/command/cli/cli-parse-serve.js +124 -0
  21. package/dist/command/cli/cli-parse.d.ts +12 -11
  22. package/dist/command/cli/cli-parse.js +100 -123
  23. package/dist/command/cli/cli-usage.d.ts +0 -11
  24. package/dist/command/cli/cli-usage.js +43 -16
  25. package/dist/command/cli/cli-verify.d.ts +1 -1
  26. package/dist/command/cli/cli-verify.js +29 -14
  27. package/dist/command/cli/cloud-cli.js +27 -9
  28. package/dist/command/cli/cloud-kit.js +3 -3
  29. package/dist/command/cli/cloud-link.d.ts +1 -20
  30. package/dist/command/cli/cloud-link.js +2 -0
  31. package/dist/command/cli/cloud-login.d.ts +6 -2
  32. package/dist/command/cli/cloud-login.js +13 -41
  33. package/dist/command/cli/config/checkout.d.ts +10 -0
  34. package/dist/command/cli/config/checkout.js +78 -0
  35. package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
  36. package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
  37. package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
  38. package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
  39. package/dist/command/cli/doctor/doctor-rows.js +4 -0
  40. package/dist/command/cli/drive/drive-attach.d.ts +5 -0
  41. package/dist/command/cli/drive/drive-attach.js +21 -1
  42. package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
  43. package/dist/command/cli/hook/codex-transcript.js +224 -0
  44. package/dist/command/cli/hook/episode-structure.d.ts +24 -0
  45. package/dist/command/cli/hook/episode-structure.js +138 -0
  46. package/dist/command/cli/hook/hook-command.d.ts +9 -0
  47. package/dist/command/cli/hook/hook-command.js +211 -0
  48. package/dist/command/cli/hook/hook-fast.d.ts +31 -0
  49. package/dist/command/cli/hook/hook-fast.js +136 -0
  50. package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
  51. package/dist/command/cli/hook/prompt-hook.js +199 -0
  52. package/dist/command/cli/hook/tool-hook.d.ts +21 -0
  53. package/dist/command/cli/hook/tool-hook.js +259 -0
  54. package/dist/command/cli/launch/cli-launch.d.ts +2 -0
  55. package/dist/command/cli/launch/cli-launch.js +22 -2
  56. package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
  57. package/dist/command/cli/mcp-command.d.ts +9 -0
  58. package/dist/command/cli/mcp-command.js +127 -41
  59. package/dist/command/cli/open-navigate.d.ts +43 -0
  60. package/dist/command/cli/open-navigate.js +85 -0
  61. package/dist/command/cli/ports/port-holder.d.ts +16 -4
  62. package/dist/command/cli/ports/port-holder.js +58 -12
  63. package/dist/command/cli/ports/resolve/cli-port.js +18 -2
  64. package/dist/command/cli/project-name.d.ts +1 -7
  65. package/dist/command/cli/project-name.js +1 -0
  66. package/dist/command/cli/report-command.d.ts +8 -2
  67. package/dist/command/cli/report-command.js +95 -18
  68. package/dist/command/cli/status-fields.d.ts +9 -2
  69. package/dist/command/cli/status-fields.js +148 -4
  70. package/dist/command/cli/try-command.d.ts +3 -4
  71. package/dist/command/cli/try-command.js +6 -11
  72. package/dist/command/cli/verify-results.d.ts +4 -1
  73. package/dist/command/cli/verify-results.js +3 -1
  74. package/dist/command/cli.d.ts +1 -7
  75. package/dist/command/cli.js +79 -160
  76. package/dist/command/daemon/daemon-resilience.d.ts +6 -1
  77. package/dist/command/daemon/daemon-resilience.js +31 -18
  78. package/dist/command/daemon/daemon.d.ts +11 -0
  79. package/dist/command/daemon/daemon.js +70 -2
  80. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
  81. package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
  82. package/dist/command/daemon/wake-decision.js +4 -2
  83. package/dist/command/daemon/win32-escape.d.ts +32 -0
  84. package/dist/command/daemon/win32-escape.js +119 -0
  85. package/dist/command/setup/agent-configs.d.ts +9 -0
  86. package/dist/command/setup/agent-configs.js +9 -0
  87. package/dist/command/setup/auto-approve.d.ts +10 -3
  88. package/dist/command/setup/auto-approve.js +51 -3
  89. package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
  90. package/dist/command/setup/bringup/dev-server-wait.js +5 -1
  91. package/dist/command/setup/bringup/ensure-daemon.js +4 -7
  92. package/dist/command/setup/init/init-host.js +6 -0
  93. package/dist/command/setup/init/init-port.js +5 -4
  94. package/dist/command/setup/init/init-runtime.js +7 -1
  95. package/dist/command/setup/node-effects.d.ts +8 -0
  96. package/dist/command/setup/node-effects.js +36 -4
  97. package/dist/command/setup/probe/dev-port.d.ts +26 -0
  98. package/dist/command/setup/probe/dev-port.js +117 -0
  99. package/dist/command/setup/probe/page-probe.d.ts +1 -1
  100. package/dist/command/setup/probe/page-probe.js +11 -3
  101. package/dist/command/setup/remaining-steps.d.ts +0 -7
  102. package/dist/command/setup/remaining-steps.js +3 -2
  103. package/dist/command/setup/run-setup.d.ts +11 -0
  104. package/dist/command/setup/run-setup.js +33 -5
  105. package/dist/command/setup/setup-command.d.ts +2 -0
  106. package/dist/command/setup/setup-command.js +22 -4
  107. package/dist/command/update/updater.d.ts +1 -6
  108. package/dist/command/update/updater.js +4 -5
  109. package/dist/command/update/version-seen.d.ts +24 -0
  110. package/dist/command/update/version-seen.js +65 -0
  111. package/dist/command/version/peer-announce.d.ts +9 -1
  112. package/dist/command/version/peer-announce.js +25 -2
  113. package/dist/command/version/sdk-fix.d.ts +5 -6
  114. package/dist/command/version/sdk-fix.js +17 -32
  115. package/dist/command/version/version-skew.d.ts +16 -1
  116. package/dist/command/version/version-skew.js +84 -10
  117. package/dist/features/crawl/crawl-tools.js +2 -2
  118. package/dist/features/crawl/crawl.js +2 -0
  119. package/dist/features/exhaust/ledger.d.ts +15 -96
  120. package/dist/features/exhaust/ledger.js +3 -3
  121. package/dist/features/harness/drive-runs.d.ts +28 -4
  122. package/dist/features/harness/drive-runs.js +99 -7
  123. package/dist/features/harness/drivers.d.ts +1 -1
  124. package/dist/features/harness/drivers.js +1 -1
  125. package/dist/features/harness/harness.d.ts +5 -0
  126. package/dist/features/harness/harness.js +24 -12
  127. package/dist/features/harness/platform/platform-drives.d.ts +7 -1
  128. package/dist/features/harness/platform/platform-drives.js +12 -7
  129. package/dist/features/harness/platform/refusal.d.ts +1 -1
  130. package/dist/features/harness/platform/refusal.js +4 -4
  131. package/dist/features/harness/platform/remote-drive.js +70 -28
  132. package/dist/features/harness/platform/server-driver.js +22 -1
  133. package/dist/features/harness/platform-config.d.ts +1 -10
  134. package/dist/features/harness/platform-config.js +0 -20
  135. package/dist/features/license/license.d.ts +2 -14
  136. package/dist/features/visual/step-shots.d.ts +33 -1
  137. package/dist/features/visual/step-shots.js +155 -1
  138. package/dist/features/visual/visual-tools.js +12 -4
  139. package/dist/hooks/coding-agents.d.ts +6 -0
  140. package/dist/hooks/coding-agents.js +24 -3
  141. package/dist/hooks/episode-stamp.d.ts +32 -0
  142. package/dist/hooks/episode-stamp.js +61 -0
  143. package/dist/hooks/hook-commands.js +19 -9
  144. package/dist/index.d.ts +5 -1
  145. package/dist/index.js +66 -24
  146. package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
  147. package/dist/judgement/app-graph/journal-steps.js +92 -0
  148. package/dist/judgement/capsule/capsule-store.d.ts +2 -39
  149. package/dist/judgement/domain/domain-model.js +1 -10
  150. package/dist/judgement/domain/domain-tools.js +4 -2
  151. package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
  152. package/dist/judgement/runs/artifact/run-store.js +153 -28
  153. package/dist/judgement/runs/default-run-id.d.ts +3 -1
  154. package/dist/judgement/runs/default-run-id.js +15 -3
  155. package/dist/judgement/runs/drive-run.d.ts +3 -3
  156. package/dist/judgement/runs/drive-run.js +11 -7
  157. package/dist/judgement/runs/run-ledger.d.ts +21 -0
  158. package/dist/judgement/runs/run-ledger.js +143 -0
  159. package/dist/judgement/runs/runner-port.d.ts +1 -1
  160. package/dist/judgement/runs/runner-port.js +8 -2
  161. package/dist/judgement/runs/verification-sync.d.ts +4 -3
  162. package/dist/judgement/runs/verification-sync.js +27 -5
  163. package/dist/language/flows/affected-tools.js +2 -1
  164. package/dist/language/flows/annotate-notes/annotate.js +8 -3
  165. package/dist/language/flows/annotate-tools.js +1 -1
  166. package/dist/language/flows/change/affected.d.ts +8 -0
  167. package/dist/language/flows/change/affected.js +20 -7
  168. package/dist/language/flows/decision.js +19 -4
  169. package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
  170. package/dist/language/flows/fields/flow-secret-field.js +67 -5
  171. package/dist/language/flows/flow-anchor.d.ts +5 -0
  172. package/dist/language/flows/flow-anchor.js +4 -0
  173. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  174. package/dist/language/flows/flow-cross-step.js +9 -0
  175. package/dist/language/flows/flow-expect-grammar.js +15 -10
  176. package/dist/language/flows/flow-intent.js +6 -1
  177. package/dist/language/flows/flow-journey.d.ts +8 -0
  178. package/dist/language/flows/flow-journey.js +13 -0
  179. package/dist/language/flows/flow-lease.d.ts +27 -0
  180. package/dist/language/flows/flow-lease.js +59 -0
  181. package/dist/language/flows/flow-replay-run.d.ts +1 -87
  182. package/dist/language/flows/flow-replay-run.js +111 -310
  183. package/dist/language/flows/flow-replay.js +21 -19
  184. package/dist/language/flows/flow-replayable.d.ts +38 -0
  185. package/dist/language/flows/flow-replayable.js +179 -0
  186. package/dist/language/flows/flow-start-path.d.ts +108 -0
  187. package/dist/language/flows/flow-start-path.js +313 -0
  188. package/dist/language/flows/flow-step-runners.d.ts +23 -2
  189. package/dist/language/flows/flow-step-runners.js +191 -32
  190. package/dist/language/flows/flow-store-for-session.d.ts +8 -1
  191. package/dist/language/flows/flow-store-for-session.js +6 -0
  192. package/dist/language/flows/flow-tools.d.ts +2 -23
  193. package/dist/language/flows/flow-tools.js +63 -113
  194. package/dist/language/flows/flows.d.ts +4 -6
  195. package/dist/language/flows/flows.js +90 -15
  196. package/dist/language/flows/heal-run.d.ts +10 -4
  197. package/dist/language/flows/heal-run.js +26 -9
  198. package/dist/language/flows/recording/flake.d.ts +3 -27
  199. package/dist/language/flows/recording/flake.js +1 -1
  200. package/dist/language/flows/recording/recording-backtrack.js +1 -4
  201. package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
  202. package/dist/language/flows/recording/tape/recordings.js +9 -3
  203. package/dist/language/flows/replay.d.ts +25 -5
  204. package/dist/language/flows/replay.js +83 -16
  205. package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
  206. package/dist/language/flows/suite/coverage.d.ts +9 -0
  207. package/dist/language/flows/suite/coverage.js +12 -1
  208. package/dist/language/flows/suite-selection.d.ts +25 -0
  209. package/dist/language/flows/suite-selection.js +41 -9
  210. package/dist/machine/git-tracks.d.ts +6 -0
  211. package/dist/machine/git-tracks.js +36 -0
  212. package/dist/memory/cloud/background-sync.d.ts +25 -0
  213. package/dist/memory/cloud/background-sync.js +79 -0
  214. package/dist/memory/cloud/cloud-config.d.ts +12 -1
  215. package/dist/memory/cloud/cloud-config.js +39 -13
  216. package/dist/memory/cloud/cloud-sync.d.ts +3 -24
  217. package/dist/memory/cloud/cloud-sync.js +5 -2
  218. package/dist/memory/cloud/harness-config.d.ts +0 -7
  219. package/dist/memory/cloud/memory-scope.d.ts +19 -0
  220. package/dist/memory/cloud/memory-scope.js +48 -0
  221. package/dist/memory/cloud/sync-answer.d.ts +43 -0
  222. package/dist/memory/cloud/sync-answer.js +42 -0
  223. package/dist/memory/cloud/sync-cycle.d.ts +40 -10
  224. package/dist/memory/cloud/sync-cycle.js +97 -144
  225. package/dist/memory/cloud/sync-daemon.d.ts +50 -3
  226. package/dist/memory/cloud/sync-daemon.js +473 -83
  227. package/dist/memory/cloud/sync-describe.d.ts +28 -0
  228. package/dist/memory/cloud/sync-describe.js +92 -0
  229. package/dist/memory/cloud/sync-disk.d.ts +9 -1
  230. package/dist/memory/cloud/sync-disk.js +137 -33
  231. package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
  232. package/dist/memory/cloud/sync-flow-versions.js +42 -0
  233. package/dist/memory/impact/impact-store.js +32 -15
  234. package/dist/memory/intent/intent-shard.d.ts +41 -266
  235. package/dist/memory/intent/intent-tools.js +20 -61
  236. package/dist/memory/intent/record-request.d.ts +27 -0
  237. package/dist/memory/intent/record-request.js +88 -0
  238. package/dist/memory/intent/settled-words.js +9 -1
  239. package/dist/memory/journal/drive-flow.d.ts +3 -0
  240. package/dist/memory/journal/drive-flow.js +11 -41
  241. package/dist/memory/journal/envelope-store.js +1 -1
  242. package/dist/memory/journal/envelope.d.ts +6 -154
  243. package/dist/memory/journal/episode-log.d.ts +107 -0
  244. package/dist/memory/journal/episode-log.js +302 -0
  245. package/dist/memory/journal/journal-recorder.d.ts +7 -0
  246. package/dist/memory/journal/journal-recorder.js +22 -9
  247. package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
  248. package/dist/memory/journal/on-disk/ambient-file.js +1 -1
  249. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
  250. package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
  251. package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
  252. package/dist/memory/journal/self-observation.js +3 -1
  253. package/dist/memory/journal/session-end.js +23 -9
  254. package/dist/memory/journal/session-journal.js +39 -20
  255. package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
  256. package/dist/memory/project/artifact-root-resolver.js +68 -23
  257. package/dist/memory/project/artifact-root.d.ts +17 -3
  258. package/dist/memory/project/artifact-root.js +47 -9
  259. package/dist/memory/project/fs/fs-port.d.ts +5 -0
  260. package/dist/memory/project/fs/fs-port.js +13 -0
  261. package/dist/memory/project/learned-routes.d.ts +3 -4
  262. package/dist/memory/project/learned-routes.js +3 -11
  263. package/dist/memory/project/link-directory.js +5 -2
  264. package/dist/memory/project/root-claims.d.ts +3 -1
  265. package/dist/memory/project/root-claims.js +41 -7
  266. package/dist/memory/project/session-root.d.ts +1 -1
  267. package/dist/memory/project/session-root.js +1 -15
  268. package/dist/memory/project/sync-status.js +5 -6
  269. package/dist/memory/recall/memory-tools.js +2 -2
  270. package/dist/memory/recall/platform-memory.d.ts +3 -1
  271. package/dist/memory/recall/platform-memory.js +9 -1
  272. package/dist/memory/recall/project-memory.js +15 -19
  273. package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
  274. package/dist/portal/bridge/auth-failure-reason.js +19 -0
  275. package/dist/portal/bridge/bridge.d.ts +19 -4
  276. package/dist/portal/bridge/bridge.js +150 -177
  277. package/dist/portal/bridge/harness-request.d.ts +15 -0
  278. package/dist/portal/bridge/harness-request.js +19 -0
  279. package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
  280. package/dist/portal/bridge/origin/origin-follow.js +110 -0
  281. package/dist/portal/bridge/pairing-token.d.ts +6 -0
  282. package/dist/portal/bridge/pairing-token.js +47 -16
  283. package/dist/portal/bridge/protocol-skew.d.ts +27 -2
  284. package/dist/portal/bridge/protocol-skew.js +66 -2
  285. package/dist/portal/bridge/session-nonces.d.ts +46 -0
  286. package/dist/portal/bridge/session-nonces.js +72 -0
  287. package/dist/portal/input/viewport-tools.d.ts +31 -0
  288. package/dist/portal/input/viewport-tools.js +106 -15
  289. package/dist/portal/pool/bounded-close.d.ts +23 -0
  290. package/dist/portal/pool/bounded-close.js +38 -0
  291. package/dist/portal/pool/browser-pool.d.ts +44 -39
  292. package/dist/portal/pool/browser-pool.js +247 -95
  293. package/dist/portal/pool/context-permissions.d.ts +9 -1
  294. package/dist/portal/pool/context-permissions.js +26 -0
  295. package/dist/portal/pool/lease-emulation.d.ts +37 -0
  296. package/dist/portal/pool/lease-emulation.js +29 -0
  297. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  298. package/dist/portal/pool/playwright-launcher.js +74 -7
  299. package/dist/portal/pool/pool-contract.d.ts +23 -1
  300. package/dist/portal/pool/pool-lease.d.ts +128 -0
  301. package/dist/portal/pool/pool-lease.js +49 -0
  302. package/dist/portal/pool/zero-install.d.ts +8 -1
  303. package/dist/portal/pool/zero-install.js +24 -2
  304. package/dist/portal/production/production-activation.d.ts +136 -0
  305. package/dist/portal/production/production-activation.js +173 -0
  306. package/dist/portal/production/production-drive.d.ts +52 -0
  307. package/dist/portal/production/production-drive.js +301 -0
  308. package/dist/portal/production/production-hello.d.ts +23 -0
  309. package/dist/portal/production/production-hello.js +23 -0
  310. package/dist/portal/realm/web-realm.js +42 -15
  311. package/dist/portal/session/control-envelope.d.ts +1 -1
  312. package/dist/portal/session/dev-server/served-document.d.ts +0 -5
  313. package/dist/portal/session/dev-server/served-document.js +6 -8
  314. package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
  315. package/dist/portal/session/first-run-wiring.d.ts +3 -36
  316. package/dist/portal/session/first-run-wiring.js +10 -103
  317. package/dist/portal/session/live-control-tools.js +61 -19
  318. package/dist/portal/session/no-session-next-action.d.ts +11 -0
  319. package/dist/portal/session/no-session-next-action.js +40 -15
  320. package/dist/portal/session/no-session-watch.d.ts +7 -0
  321. package/dist/portal/session/no-session-watch.js +67 -23
  322. package/dist/portal/session/output-budget.d.ts +10 -0
  323. package/dist/portal/session/output-budget.js +16 -0
  324. package/dist/portal/session/presence/session-lease.d.ts +2 -0
  325. package/dist/portal/session/presence/session-lease.js +8 -0
  326. package/dist/portal/session/recorded-gaps.d.ts +15 -11
  327. package/dist/portal/session/recorded-gaps.js +39 -18
  328. package/dist/portal/session/serving-directory.d.ts +24 -2
  329. package/dist/portal/session/serving-directory.js +93 -5
  330. package/dist/portal/session/session-info.d.ts +26 -0
  331. package/dist/portal/session/session-info.js +20 -0
  332. package/dist/portal/session/session-manager.d.ts +35 -3
  333. package/dist/portal/session/session-manager.js +69 -14
  334. package/dist/portal/session/session-successor.js +1 -9
  335. package/dist/portal/session/session-tools.js +22 -4
  336. package/dist/portal/session/session.d.ts +10 -10
  337. package/dist/portal/session/session.js +37 -29
  338. package/dist/portal/session/zero-install-first-call.d.ts +7 -2
  339. package/dist/portal/session/zero-install-first-call.js +43 -20
  340. package/dist/start-wiring.d.ts +2 -2
  341. package/dist/start-wiring.js +17 -3
  342. package/dist/status-payload.d.ts +11 -1
  343. package/dist/status-payload.js +7 -1
  344. package/dist/surface/agent-presence-grace.d.ts +11 -0
  345. package/dist/surface/agent-presence-grace.js +34 -0
  346. package/dist/surface/http-server.d.ts +12 -1
  347. package/dist/surface/http-server.js +64 -15
  348. package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
  349. package/dist/surface/mcp/faults/mcp-outage.js +19 -1
  350. package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
  351. package/dist/surface/mcp/mcp-proxy.js +194 -267
  352. package/dist/surface/mcp/mcp.d.ts +20 -3
  353. package/dist/surface/mcp/mcp.js +88 -25
  354. package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
  355. package/dist/surface/mcp/proxy/drive-calls.js +114 -5
  356. package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
  357. package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
  358. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
  359. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
  360. package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
  361. package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
  362. package/dist/surface/mcp/server-instructions.js +35 -16
  363. package/dist/surface/mcp/shared-params.d.ts +8 -0
  364. package/dist/surface/mcp/shared-params.js +15 -1
  365. package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
  366. package/dist/surface/mcp/sse-frame-parser.js +18 -0
  367. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  368. package/dist/surface/mcp/surface-vocabulary.js +13 -0
  369. package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
  370. package/dist/surface/mcp/tool-catalog-cache.js +14 -4
  371. package/dist/surface/tools/account-tool.d.ts +4 -3
  372. package/dist/surface/tools/account-tool.js +20 -31
  373. package/dist/surface/tools/act/act-danger.d.ts +2 -0
  374. package/dist/surface/tools/act/act-danger.js +41 -14
  375. package/dist/surface/tools/act/act-observation.d.ts +11 -0
  376. package/dist/surface/tools/act/act-observation.js +33 -0
  377. package/dist/surface/tools/act/act-preflight.js +10 -3
  378. package/dist/surface/tools/act/act-sequence-retry.js +2 -1
  379. package/dist/surface/tools/act/act-target.js +19 -5
  380. package/dist/surface/tools/act/durable.js +1 -1
  381. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
  382. package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
  383. package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
  384. package/dist/surface/tools/act/unsettled-find.js +71 -0
  385. package/dist/surface/tools/act-sequence-tool.js +53 -24
  386. package/dist/surface/tools/act-tools.d.ts +2 -3
  387. package/dist/surface/tools/act-tools.js +49 -47
  388. package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
  389. package/dist/surface/tools/agent/for-the-agent.js +41 -3
  390. package/dist/surface/tools/assert/assert-grade.js +8 -3
  391. package/dist/surface/tools/assert/assert-verdict.js +4 -2
  392. package/dist/surface/tools/browser-tools.js +7 -4
  393. package/dist/surface/tools/chat-drives.js +50 -25
  394. package/dist/surface/tools/coverage-tools.js +1 -1
  395. package/dist/surface/tools/dynamic-tools.d.ts +8 -0
  396. package/dist/surface/tools/dynamic-tools.js +89 -34
  397. package/dist/surface/tools/empty-session-list.d.ts +18 -25
  398. package/dist/surface/tools/empty-session-list.js +67 -38
  399. package/dist/surface/tools/error-recovery.d.ts +17 -7
  400. package/dist/surface/tools/error-recovery.js +53 -20
  401. package/dist/surface/tools/explore-tools.d.ts +10 -0
  402. package/dist/surface/tools/explore-tools.js +72 -19
  403. package/dist/surface/tools/feedback-tools.js +23 -2
  404. package/dist/surface/tools/gap-novelty.d.ts +6 -0
  405. package/dist/surface/tools/gap-novelty.js +9 -0
  406. package/dist/surface/tools/gaps/capability-absences.js +4 -3
  407. package/dist/surface/tools/harness-explore.d.ts +1 -1
  408. package/dist/surface/tools/harness-explore.js +6 -4
  409. package/dist/surface/tools/harness-gate.d.ts +43 -21
  410. package/dist/surface/tools/harness-gate.js +55 -81
  411. package/dist/surface/tools/harness-script.d.ts +2 -0
  412. package/dist/surface/tools/harness-script.js +11 -6
  413. package/dist/surface/tools/harness-toolset.js +3 -4
  414. package/dist/surface/tools/invoke-tool.d.ts +0 -8
  415. package/dist/surface/tools/invoke-tool.js +164 -178
  416. package/dist/surface/tools/lease-hint.d.ts +9 -0
  417. package/dist/surface/tools/lease-hint.js +22 -3
  418. package/dist/surface/tools/lease-permissions.d.ts +1 -1
  419. package/dist/surface/tools/lease-readiness.d.ts +55 -0
  420. package/dist/surface/tools/lease-readiness.js +150 -2
  421. package/dist/surface/tools/lease-tools.d.ts +3 -75
  422. package/dist/surface/tools/lease-tools.js +124 -188
  423. package/dist/surface/tools/map-tools.d.ts +20 -0
  424. package/dist/surface/tools/map-tools.js +145 -0
  425. package/dist/surface/tools/merge-tools.js +11 -7
  426. package/dist/surface/tools/next-step.js +6 -3
  427. package/dist/surface/tools/observe-tools.js +46 -35
  428. package/dist/surface/tools/platform-moment.d.ts +3 -12
  429. package/dist/surface/tools/platform-moment.js +12 -24
  430. package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
  431. package/dist/surface/tools/read/complete-snapshot.js +13 -2
  432. package/dist/surface/tools/read/query-paginate.js +6 -0
  433. package/dist/surface/tools/read/query-shape.d.ts +9 -0
  434. package/dist/surface/tools/read/query-shape.js +15 -0
  435. package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
  436. package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
  437. package/dist/surface/tools/read-tools.js +5 -5
  438. package/dist/surface/tools/real-input-attempt.d.ts +11 -0
  439. package/dist/surface/tools/real-input-attempt.js +206 -38
  440. package/dist/surface/tools/reconcile-tools.js +21 -8
  441. package/dist/surface/tools/reticle-result.d.ts +3 -0
  442. package/dist/surface/tools/reticle-result.js +8 -1
  443. package/dist/surface/tools/run-report.d.ts +22 -0
  444. package/dist/surface/tools/run-report.js +141 -0
  445. package/dist/surface/tools/session-ports.d.ts +57 -0
  446. package/dist/surface/tools/session-ports.js +102 -0
  447. package/dist/surface/tools/tool-kit.d.ts +58 -4
  448. package/dist/surface/tools/tool-kit.js +140 -6
  449. package/dist/surface/tools/tool-surface.d.ts +2 -0
  450. package/dist/surface/tools/tool-surface.js +45 -0
  451. package/dist/surface/tools/tools.js +49 -87
  452. package/dist/telemetry/automation-hint.d.ts +5 -0
  453. package/dist/telemetry/automation-hint.js +7 -0
  454. package/dist/telemetry/cli-telemetry.d.ts +22 -0
  455. package/dist/telemetry/cli-telemetry.js +80 -11
  456. package/dist/telemetry/daemon-telemetry.d.ts +3 -1
  457. package/dist/telemetry/daemon-telemetry.js +9 -2
  458. package/dist/telemetry/feedback-cli.d.ts +1 -1
  459. package/dist/telemetry/feedback-cli.js +8 -3
  460. package/dist/telemetry/feedback.d.ts +1 -1
  461. package/dist/telemetry/feedback.js +51 -4
  462. package/dist/telemetry/harness-run.d.ts +3 -3
  463. package/dist/telemetry/harness-run.js +18 -7
  464. package/dist/telemetry/hud-events.d.ts +1 -1
  465. package/dist/telemetry/hud-metrics.d.ts +6 -3
  466. package/dist/telemetry/hud-metrics.js +20 -8
  467. package/dist/telemetry/install-source.js +1 -1
  468. package/dist/telemetry/mcp-connection.d.ts +1 -1
  469. package/dist/telemetry/mcp-connection.js +21 -2
  470. package/dist/telemetry/onboarding-firsts.d.ts +6 -1
  471. package/dist/telemetry/onboarding-firsts.js +41 -1
  472. package/dist/telemetry/proxy-telemetry.d.ts +42 -0
  473. package/dist/telemetry/proxy-telemetry.js +70 -1
  474. package/dist/telemetry/session-metrics.d.ts +1 -3
  475. package/dist/telemetry/session-metrics.js +16 -12
  476. package/dist/telemetry/telemetry.d.ts +9 -1
  477. package/dist/telemetry/telemetry.js +16 -1
  478. package/dist/telemetry/verification-of.d.ts +3 -1
  479. package/dist/telemetry/verification-of.js +7 -1
  480. package/dist/wire-journal.js +12 -2
  481. package/docs/README.md +4 -2
  482. package/docs/actions.mdx +7 -6
  483. package/docs/adapters.md +2 -1
  484. package/docs/agent-cheatsheet.md +19 -5
  485. package/docs/architecture.md +13 -10
  486. package/docs/autodrive.md +6 -5
  487. package/docs/benchmarks.md +2 -2
  488. package/docs/capabilities.mdx +1 -1
  489. package/docs/cli/affected.mdx +1 -1
  490. package/docs/cli/cloud.mdx +6 -6
  491. package/docs/cli/doctor.mdx +3 -1
  492. package/docs/cli/feedback.mdx +11 -7
  493. package/docs/cli/gate.mdx +1 -1
  494. package/docs/cli/help.mdx +1 -1
  495. package/docs/cli/init.mdx +20 -16
  496. package/docs/cli/mcp.mdx +5 -2
  497. package/docs/cli/open.mdx +5 -2
  498. package/docs/cli/report.mdx +1 -1
  499. package/docs/cli/restart.mdx +1 -1
  500. package/docs/cli/rollback.mdx +4 -4
  501. package/docs/cli/serve.mdx +7 -5
  502. package/docs/cli/setup.mdx +1 -1
  503. package/docs/cli/status.mdx +6 -2
  504. package/docs/cli/stop.mdx +3 -3
  505. package/docs/cli/telemetry.mdx +5 -1
  506. package/docs/cli/try.mdx +8 -8
  507. package/docs/cli/update.mdx +8 -5
  508. package/docs/cli/verify.mdx +12 -5
  509. package/docs/cli/version.mdx +1 -1
  510. package/docs/cli.mdx +10 -6
  511. package/docs/debugging.md +1 -1
  512. package/docs/deploy-checks.md +8 -6
  513. package/docs/desktop.mdx +14 -18
  514. package/docs/enterprise.md +6 -6
  515. package/docs/environment.mdx +5 -3
  516. package/docs/faq.mdx +9 -6
  517. package/docs/first-drive.md +19 -9
  518. package/docs/flows.md +3 -4
  519. package/docs/for-agents.md +1 -1
  520. package/docs/frameworks.mdx +34 -11
  521. package/docs/getting-started.md +46 -29
  522. package/docs/http-transport.md +2 -2
  523. package/docs/human-control.md +8 -8
  524. package/docs/index.mdx +2 -2
  525. package/docs/install-agentic.mdx +18 -19
  526. package/docs/install-manual.mdx +8 -4
  527. package/docs/integration-patterns.md +5 -5
  528. package/docs/multi-agent-testing.md +7 -5
  529. package/docs/packages/browser.mdx +34 -2
  530. package/docs/packages/core.mdx +2 -1
  531. package/docs/packages/electron.mdx +15 -3
  532. package/docs/packages/eslint-plugin.mdx +1 -1
  533. package/docs/packages/next.mdx +17 -5
  534. package/docs/packages/react.mdx +8 -1
  535. package/docs/packages/server.mdx +3 -3
  536. package/docs/packages/tauri.mdx +3 -3
  537. package/docs/packages/vite-plugin.mdx +1 -1
  538. package/docs/packages.mdx +19 -6
  539. package/docs/platform-integration.md +12 -4
  540. package/docs/predicates.mdx +18 -7
  541. package/docs/production-mode.md +108 -0
  542. package/docs/prompt-capture.md +99 -0
  543. package/docs/quickstart.mdx +14 -12
  544. package/docs/recipes.mdx +9 -8
  545. package/docs/required-checks.md +3 -2
  546. package/docs/skill-file.mdx +4 -3
  547. package/docs/skill-setup.md +2 -2
  548. package/docs/skill.md +62 -70
  549. package/docs/state-files.md +1 -0
  550. package/docs/state-management.mdx +11 -9
  551. package/docs/system-map.md +14 -13
  552. package/docs/telemetry.md +32 -24
  553. package/docs/testing.md +9 -5
  554. package/docs/tools/act-and-wait.mdx +17 -13
  555. package/docs/tools/act-sequence.mdx +40 -66
  556. package/docs/tools/act.mdx +7 -5
  557. package/docs/tools/assert.mdx +1 -1
  558. package/docs/tools/look.mdx +1 -1
  559. package/docs/tools/navigate.mdx +1 -1
  560. package/docs/tools/network.mdx +13 -11
  561. package/docs/tools/overview.mdx +3 -3
  562. package/docs/tools/query.mdx +3 -3
  563. package/docs/tools/session-and-feedback.mdx +9 -6
  564. package/docs/tools/sessions.mdx +7 -0
  565. package/docs/tools/snapshot.mdx +9 -7
  566. package/docs/tools/tools-and-run.mdx +11 -10
  567. package/docs/tools/verify.mdx +1 -1
  568. package/docs/troubleshooting.mdx +21 -9
  569. package/docs/usage.md +58 -43
  570. package/docs/use-cases.mdx +2 -2
  571. package/docs/vs-chrome-devtools-mcp.mdx +5 -5
  572. package/docs/vs-playwright-mcp.mdx +5 -4
  573. package/docs/what-is-recorded.md +12 -7
  574. package/docs/what-is-reticle.mdx +2 -2
  575. package/docs/why-reticle.mdx +1 -1
  576. package/package.json +9 -12
  577. package/dist/command/dev/stale-issue-guard.d.ts +0 -34
  578. package/dist/command/dev/stale-issue-guard.js +0 -69
package/docs/cli.mdx CHANGED
@@ -48,7 +48,7 @@ npx @reticlehq/server <command>
48
48
  | Command | What it does | Page |
49
49
  | --- | --- | --- |
50
50
  | `reticle status [--json]` | Is the daemon up, which pages connected, who is signed in, is this repo linked | [status](/cli/status) |
51
- | `reticle doctor` | Diagnose setup: Chromium, daemon, port | [doctor](/cli/doctor) |
51
+ | `reticle doctor` | Find what is wrong with the setup (Chromium, daemon, port, agent link), and what is missing from coverage | [doctor](/cli/doctor) |
52
52
  | `reticle open [url]` | Show the app: reuse the connected tab, else open one | [open](/cli/open) |
53
53
  | `reticle stop [--force]` | Stop the daemon we started; `--force` frees the port by its listener, never the agent's MCP proxy | [stop](/cli/stop) |
54
54
  | `reticle restart` | Free the port, then start a daemon and wait for a real bind | [restart](/cli/restart) |
@@ -73,6 +73,7 @@ npx @reticlehq/server <command>
73
73
  | `reticle runs issues [--fix <fp>]` | The triage queue; `--fix` prints one issue's fix prompt | [cloud](/cli/cloud#reticle-runs-issues) |
74
74
  | `reticle runs memory` | What the linked project has learned | [cloud](/cli/cloud#reticle-runs-memory) |
75
75
  | `reticle config` | Choose what syncs: runs, memory, flows; where verification runs | [cloud](/cli/cloud#reticle-config) |
76
+ | `reticle hook [status\|install\|uninstall]` | The prompt hook: is each prompt captured here, add it, or remove exactly what was added | [prompt capture](/prompt-capture#reticle-hook) |
76
77
  | `reticle setup mcp` | Register the MCP server with your coding agents (the installer's registration half) | [setup](/cli/setup) |
77
78
  | `reticle serve` | Start the daemon and wait for it to bind | [serve](/cli/serve) |
78
79
  | `reticle mcp` | The MCP stdio proxy your agent runs; starts the daemon if needed | [mcp](/cli/mcp) |
@@ -92,7 +93,7 @@ The old names below still work for one release and print one line naming the new
92
93
  | Old | Now |
93
94
  | --- | --- |
94
95
  | `reticle kill` | `reticle stop --force` |
95
- | `reticle login`, `reticle link` | `reticle connect` |
96
+ | `reticle login`, `reticle link` | `reticle connect`. `reticle login --email` is gone: sign-in is only in the browser, with Google or GitHub. Without a browser, set `RETICLE_API_KEY` |
96
97
  | `reticle project` | `reticle connect --project <name\|id>` |
97
98
  | `reticle whoami` | `reticle status` |
98
99
  | `reticle push` | `reticle sync` |
@@ -121,7 +122,7 @@ reticle init [--env KEY=VALUE]... [--app <dir>] [--license <key>]
121
122
  [--json] [--files-only] [--relaunch] [--no-open] [--no-agents]
122
123
  [--url <app url>] [--timeout S] [--dry-run] [--hooks]
123
124
  [--port N] [--no-mcp] [--no-install] [--start]
124
- [--capture-bodies] [--no-first-run]
125
+ [--capture-bodies] [--no-first-run] [--new-id] [--no-prompt-capture]
125
126
  ```
126
127
 
127
128
  `--env` and `--app` are the answers the command cannot work out for itself: what the app needs to reach a usable state, and which app in a monorepo. Which journey proves what you care about is asked on the first run, as `--persona`. `--files-only` is what `init` did before it learned to boot the app: it still registers the MCP server and pre-approves Reticle's tools, so it doubles as the upgrade path for an existing install.
@@ -142,7 +143,7 @@ npx @reticlehq/server doctor
142
143
  reticle doctor
143
144
  node v22.14.0
144
145
  chromium ✓ installed (chromium-1234)
145
- daemon ✓ running on :4400 (pid 72698, v3.7.0)
146
+ daemon ✓ running on :4400 (pid 72698, v3.8.0)
146
147
  sessions ✓ 1 page connected
147
148
  bridge port 4400 (your app must dial THIS port, not your dev-server port)
148
149
  agent link ✓ an MCP client attached here and called tools before (a record, not a live check)
@@ -162,7 +163,7 @@ reticle status
162
163
  daemon ✓ running on :4400 (pid 90790)
163
164
  sessions ✓ 1 page connected
164
165
  http://localhost:4310/
165
- agent link enumerated
166
+ agent link ✓ your agent has read and used Reticle's tools
166
167
  ```
167
168
 
168
169
  Add `--json` for the same facts as one machine-readable event line. See [`reticle status`](/cli/status) for the fields.
@@ -232,7 +233,7 @@ Options:
232
233
  - `--storage-state <file>`: verify flows behind a login without scripting the login every time.
233
234
  - `--session-id <id>`: use a tab that is already connected.
234
235
  - `--select <label>`: verify only flows carrying this label. Repeatable.
235
- - `--expect '<json predicate>'` or `--expect-file <path>`: one verdict, no saved flows needed. Asks the running daemon; with none, it refuses. Exits `0` only on `verified: "yes"`.
236
+ - `--expect '<json predicate>'` or `--expect-file <path>`: one verdict, no saved flows needed. Asks the running daemon; with none, it refuses. Waits up to `--timeout` ms (default `4000`) for the predicate to come true. Exits `0` only on `verified: "yes"`.
236
237
  - `--explore` with `--persona "<who does what>"`: no saved flows yet? The Harness drives the app and records them. Needs a linked project (`reticle connect`).
237
238
  - `--results-json <file>`: also write one verdict per journey as JSON, for CI.
238
239
  - `--confirm-dangerous`: let this run press what the replay guard holds as destructive.
@@ -308,6 +309,9 @@ Measured exit code: **`1`**, with the flow lists trimmed above. That run genuine
308
309
  # humans
309
310
  npx @reticlehq/server feedback "what worked, what didn't"
310
311
 
312
+ # humans who want a reply: one email address, sent only when you give it
313
+ npx @reticlehq/server feedback --contact you@example.com "what worked, what didn't"
314
+
311
315
  # agents, including from a setup that never finished
312
316
  npx @reticlehq/server feedback --agent --kind <bug|gap|ambiguity|feature_request|improvement|experience> "what happened"
313
317
  ```
package/docs/debugging.md CHANGED
@@ -100,7 +100,7 @@ Two things that trace makes visible and nothing else did. Planning is ~2ms, so i
100
100
  ### Adding a span
101
101
 
102
102
  ```ts
103
- import { span } from '../trace.js';
103
+ import { span } from '@/trace.js';
104
104
 
105
105
  const result = await span('predicate.eval', { kind: predicate.kind }, () => evaluate(predicate));
106
106
  ```
@@ -4,9 +4,9 @@ description: 'Enforce verification at the moment code becomes a preview URL, as
4
4
  icon: circle-check
5
5
  ---
6
6
 
7
- To gate a PR on Reticle, run `npx @reticlehq/server verify <preview-url>` in CI when the preview deploy succeeds. It exits **0** when every saved flow passes and **1** otherwise, so the job's own pass/fail becomes the check on the PR. There is no workflow-level integration to write beyond that one command.
7
+ To gate a PR on Reticle, run `npx @reticlehq/server verify <preview-url>` in CI when the preview deploy succeeds. It exits **0** when every saved flow passes and **1** otherwise, so the job's own pass/fail becomes the check on the PR.
8
8
 
9
- The strongest place to enforce verification is the moment code becomes a preview URL. Every deploy already produces one, and both Vercel and Netlify let a third party attach a **check** to it: pass/fail, shown on the PR, with no workflow file to write.
9
+ The strongest place to enforce verification is the moment code becomes a preview URL. Every deploy already makes one. Today you run Reticle from a CI job on the deploy event (Recipe A). A hosted check provider that needs no workflow file does not exist yet (Recipe B).
10
10
 
11
11
  That makes the non-developer story complete: the SDK is auto-injected by the build plugin, flows are minted from toolbar recordings with auto-proposed consequences, and verification runs at publish, **without anyone writing a test.**
12
12
 
@@ -43,9 +43,11 @@ jobs:
43
43
  node-version: 22
44
44
  cache: pnpm
45
45
  - run: pnpm install --frozen-lockfile
46
- - run: npx playwright install --with-deps chromium
46
+ - run: npm install -g @reticlehq/server
47
+ # Install the Chromium that Reticle's own Playwright uses, not the latest one.
48
+ - run: cd "$(npm root -g)/@reticlehq/server" && npx playwright install --with-deps chromium
47
49
  # Reticle drives the preview and owns its own browser + daemon.
48
- - run: npx @reticlehq/server verify "${{ github.event.deployment_status.target_url }}" --timeout 60000
50
+ - run: reticle verify "${{ github.event.deployment_status.target_url }}" --timeout 60000
49
51
  ```
50
52
 
51
53
  The job's own pass/fail becomes the PR check. Nothing else is required.
@@ -71,7 +73,7 @@ The deploy check catches what reaches a preview. `reticle gate` catches it earli
71
73
  npx @reticlehq/server gate --since origin/main # exit 1 unless passing artifacts cover the affected flows
72
74
  ```
73
75
 
74
- Use both: `gate` in the agent's Stop hook (see `agent-cheatsheet`), `verify` at the deploy. They read the same run artifacts, so a green gate locally and a green check on the PR mean the same thing.
76
+ Use both: `gate` in the agent's Stop hook (see the [agent cheat sheet](agent-cheatsheet.md)), `verify` at the deploy. They read the same run artifacts, so a green gate locally and a green check on the PR mean the same thing.
75
77
 
76
78
  ## Honest limits
77
79
 
@@ -99,4 +101,4 @@ If you want an agent to be unable to call a change finished without re-verifying
99
101
 
100
102
  ### verify exited 1 but I cannot see which flow failed.
101
103
 
102
- The verdict is in the run artifact, not only in the log. Each run writes `.reticle/runs/<id>.json` with `repair.failurePackets[]` naming what broke and where; `reticle_run_export { format: "report" }` renders it legibly. The artifact is stable and versioned precisely so a CI surface can render it without parsing log lines.
104
+ The verdict is in the run artifact, not only in the log. Each run writes `.reticle/runs/<id>.json` with `repair.failurePackets[]` naming what broke and where; `reticle_run({ tool: "reticle_run_export", args: { format: "report" } })` prints it as a ✓/✗ report. The artifact is stable and versioned precisely so a CI surface can render it without parsing log lines. Each entry in `checks[]` carries `intent`, a sentence a person can read: the agent's own `intent` when it gave one, else a sentence built from the predicate in the failure voice ("No PATCH request to /assignee was made"), marked `intentDerived: true`. A failed `allOf` also carries `failedPart`, the index of the part that failed, so a title can name the one thing that did not happen.
package/docs/desktop.mdx CHANGED
@@ -122,7 +122,7 @@ The rows in **bold** are re-proven by a committed battery on every change, `pnpm
122
122
 
123
123
  Two steps. The first is the ordinary web setup; the second is the only desktop-specific part.
124
124
 
125
- **1. The renderer.** One line in `vite.config.ts`, exactly like a web app:
125
+ **1. The renderer.** One line in `vite.config.ts`, exactly like a web app. With electron-vite, put it in the `renderer` block of `electron.vite.config`, not in `main` or `preload`:
126
126
 
127
127
  ```ts
128
128
  import { reticle } from '@reticlehq/vite-plugin';
@@ -135,7 +135,7 @@ export default defineConfig({
135
135
 
136
136
  `desktop: true` does two things a desktop shell needs: `connect()` is injected into the renderer's entry module, so a renderer loaded with no dev server still resolves it, and it is called with `allowInProduction`, because a packaged renderer reports `NODE_ENV=production` and the SDK's production backstop would otherwise refuse to start. It instruments the dev server, and a `vite build` **only when that build runs in a non-production mode**: `vite build --mode development`, for a packaged renderer you want to drive with no dev server. A production-mode build, which is what `vite build` and `electron-vite build` do by default, is stubbed exactly like a web bundle: it carries no SDK and never dials `ws://localhost`, so a release binary built the normal way is clean.
137
137
 
138
- Nothing to add in your app code. You can still call `reticle.connect()` by hand and pass `inject: false` if you want control.
138
+ Nothing to add in your app code. You can still call `reticle.connect()` by hand and pass `inject: false` if you want control. Then pass the pairing token yourself: `reticle.connect({ token: __RETICLE_TOKEN__ })`. The plugin defines `__RETICLE_TOKEN__`, and the bridge refuses a connect without the token, also on localhost.
139
139
 
140
140
  **2. The preload.** One line, before you expose anything:
141
141
 
@@ -163,15 +163,21 @@ Working example: [`apps/electron-smoke`](https://github.com/reticlehq/reticle/tr
163
163
 
164
164
  **Electron Forge.** `reticle init` recognises Forge's Vite template (`vite.renderer.config.*` and no `vite.config.*`) and wires it the same three ways: `reticle()` in the renderer config, the preload shim, and `installReticleCapture` in main. The plugin goes in **without** `desktop: true`, because `electron-forge start` serves the renderer through a real Vite dev server, so the ordinary serve-time injection reaches the window and `electron-forge package` ships a renderer with no Reticle code. `init` starts the app with the script that runs `electron-forge start`, not the renderer's dev script. A vanilla renderer gets `@reticlehq/browser` rather than the React kit.
165
165
 
166
+ **A static renderer (no Vite).** When the renderer is a plain `index.html`, `reticle init` does not write the connect snippet into it. A packaged Electron app can serve its renderer on `localhost`, so a localhost-only guard does not keep the SDK out of a release. Init prints a manual step instead. Connect only in development: for example, send `!app.isPackaged` from the main process to the renderer, and call `reticle.connect()` only when it is true. Pass the pairing token as `token`: the bridge refuses a connect without it. Use `@reticlehq/electron` for IPC and capture. If `reticle.local.js` is in the project, keep it out of the package: electron-builder `files: ["!reticle.local.js"]`, or Electron Forge `packagerConfig.ignore: [/reticle\.local\.js$/]`.
167
+
166
168
  ## Tauri setup
167
169
 
168
- Frontend side, nothing desktop-specific:
170
+ Frontend side: add the Vite plugin with `desktop: true`, as for Electron. The plugin injects `connect()` with this machine's pairing token. Do not call `reticle.connect()` yourself.
169
171
 
170
172
  ```ts
171
- // src/main.tsx
172
- import { reticle } from '@reticlehq/browser';
173
+ // vite.config.ts
174
+ import { defineConfig } from 'vite';
175
+ import react from '@vitejs/plugin-react';
176
+ import { reticle } from '@reticlehq/vite-plugin';
173
177
 
174
- if (import.meta.env.DEV) reticle.connect();
178
+ export default defineConfig({
179
+ plugins: [react(), reticle({ desktop: true })],
180
+ });
175
181
  ```
176
182
 
177
183
  Nothing else is needed for IPC. A Tauri `invoke` travels as a real `fetch` to Tauri's `ipc://` custom protocol, so Reticle already sees it; every `invoke('load_todos')` shows up as `ipc://load_todos`. Reticle also reads Tauri's `Tauri-Response` header, because the transport answers **HTTP 200 whether the Rust command returned `Ok` or `Err`**. Without that translation a failed command would be recorded as a successful request.
@@ -218,7 +224,7 @@ Alternatively, since an Electron renderer _is_ Chromium, `--remote-debugging-por
218
224
 
219
225
  ```toml
220
226
  [dependencies]
221
- reticle-tauri = "3.7"
227
+ reticle-tauri = "3.8"
222
228
  ```
223
229
 
224
230
  ```rust
@@ -227,7 +233,7 @@ tauri::Builder::default()
227
233
  .on_page_load(reticle_tauri::on_page_load)
228
234
  ```
229
235
 
230
- **Dev builds only.** In a release build the crate is inert: `reticle_capture` refuses and `on_page_load` does nothing, so the binary you sign and ship carries no Reticle capability. A release build you test with Reticle (a CI fixture) turns it on with `reticle-tauri = { version = "3.7", features = ["in-release"] }`.
236
+ **Dev builds only.** In a release build the crate is inert: `reticle_capture` refuses and `on_page_load` does nothing, so the binary you sign and ship carries no Reticle capability. A release build you test with Reticle (a CI fixture) turns it on with `reticle-tauri = { version = "3.8", features = ["in-release"] }`.
231
237
 
232
238
  Nothing on the JavaScript side: the SDK invokes the command through Tauri's own internals, because Tauri has no preload stage where a shim could be installed.
233
239
 
@@ -273,16 +279,6 @@ The ordering is the whole trick. Hiding the window during `setup` hides it befor
273
279
 
274
280
  `xvfb-run -a pnpm tauri dev` also works on Linux and needs no app-side change at all.
275
281
 
276
- ### A correction worth recording: the Tauri macOS "liveness constraint" was wrong
277
-
278
- Earlier versions of this document said a Tauri app on macOS is only drivable while its window is on the active Space and unoccluded, and that hiding it suspends the webview. **That is not true, and the mistake is worth recording because it cost three features.**
279
-
280
- Re-measured against the live app, a loaded Tauri webview answers Reticle commands at full speed while minimized, app-hidden with Cmd-H, fully occluded, on another Space behind a fullscreen app, and with no window on screen at all. A full 43-tool drive passes in every one of those states.
281
-
282
- What actually failed was narrower: **a webview that has never been presented never loads its page.** Every "suspension" experiment hid or moved the window from `setup`, meaning before the first present, so the page never ran and every command timed out at 8s. The timeouts were real; the diagnosis was not. The `alwaysOnTop` workaround was then built to fix a problem that did not exist, measured as "still broken" for the same reason, and deleted.
283
-
284
- The lesson generalises past this document: four experiments agreeing does not make a conclusion controlled, if all four share the same confound.
285
-
286
282
  ## Routing: use a hash router
287
283
 
288
284
  A packaged renderer runs on `file://`, where `pushState('/settings')` rewrites the URL to `file:///settings`, a path that does not exist, so the next reload lands on a blank page and the app is gone. This is why HashRouter is the standard choice for packaged Electron and Tauri apps. Reticle's route observer handles both, and a `{ kind: 'route', contains: ... }` assertion matches the fragment.
@@ -4,15 +4,15 @@ description: 'Premium access and activation, what is gated, the security and dat
4
4
  icon: building
5
5
  ---
6
6
 
7
- Enterprise features ship inside the open package and are unlocked by a signed license key you set as `RETICLE_LICENSE_KEY`. Activation is verified locally with Ed25519 and makes **no network call**, so nothing about your usage leaves your machine. Run `npx @reticlehq/server license` to see whether you are `active`, `eval`, `missing`, `expired` or `invalid`.
7
+ Enterprise features ship inside the open package and are unlocked by a signed license key you set as `RETICLE_LICENSE_KEY`. Activation is verified locally with Ed25519 and makes **no network call**. Usage metrics are separate: they are on by default, and on a licensed build they carry your licence id. Turn them off with `RETICLE_TELEMETRY=0`. See [telemetry](/telemetry). Run `npx @reticlehq/server license` to see whether you are `active`, `eval`, `missing`, `expired` or `invalid`.
8
8
 
9
- > Premium access (how you get + activate it), what's gated, the security/data-handling posture, and the licensing model. Integration mechanics live in [`platform-integration.md`](./platform-integration.md).
9
+ > For how to connect a platform to Reticle, see [Platform integration](/platform-integration).
10
10
 
11
11
  > **Already have a key and just need to switch it on?** [Activating a license key](./license-activation) is the step-by-step version: where to set it on each platform, the editor and CI cases, how to read every status, and what to do when it does not work.
12
12
 
13
13
  ## How premium access works (offline, no phone-home)
14
14
 
15
- Enterprise (`ee/`) features ship **inside the open package**; they're source-available (free for development, testing, and evaluation). A **license key activates them in production**. Activation is verified locally with Ed25519; nothing about your usage ever leaves your machine.
15
+ Enterprise (`ee/`) features ship **inside the open package**; they're source-available (free for development, testing, and evaluation). A **license key activates them in production**. Activation is verified locally with Ed25519 and sends nothing. Usage metrics, which you can turn off, carry your licence id on a licensed build.
16
16
 
17
17
  **The flow, end to end:**
18
18
 
@@ -46,7 +46,7 @@ The licensing **mechanism** is open core (inspectable, FSL). Only the **features
46
46
  - **Today:** the activation gate + one gated feature (audit event recording, `audit-log`).
47
47
  - **Roadmap** (the reliably enterprise-only set): **SSO/SAML, SCIM, RBAC / team permissions, audit logs, multi-org management, verify-before-merge policy gates, and the hosted control-plane connectors.** These are the things a security/compliance org pays for; the core verification engine stays free forever.
48
48
 
49
- > What's premium vs free, and pricing, are business decisions for the owner; this doc describes the _mechanism_, not the price list.
49
+ > For pricing, write to [hey@reticle.sh](mailto:hey@reticle.sh). This page describes the mechanism, not the price list.
50
50
 
51
51
  ## Security & data handling
52
52
 
@@ -54,9 +54,9 @@ The honest one-pager a security review needs. Reticle is built so the answer to
54
54
 
55
55
  | Question | Answer |
56
56
  | --- | --- |
57
- | Does the SDK ship to production? | **No.** Dev/preview-only, tree-shaken from production builds. |
57
+ | Does the SDK ship to production? | **Not by default.** The dev SDK is dev/preview-only and tree-shaken from production builds. If you opt in to [production mode](/production-mode), the `@reticlehq/browser/activate` loader ships. A visitor's page load reads one query parameter and loads no SDK, opens no socket and writes nothing. Only a signed, single-use ticket loads the SDK. |
58
58
  | Where does the server run? | **Localhost.** The bridge binds `127.0.0.1`; the verify endpoint is localhost-bound + token-guarded (constant-time), with request/body-size/timeout limits. |
59
- | Does anything phone home? | **No app data unless you opt in.** Linking a project to the dashboard (`reticle connect`) sends its runs there; see [what is recorded](what-is-recorded.md) and `reticle config` for what it sends. License checks are offline (Ed25519). The CLI sends anonymous, opt-out usage metrics only (random id + event names, no code, no PII; see [telemetry](telemetry.md)); disable fleet-wide with `RETICLE_TELEMETRY=0` or per-machine with `reticle telemetry disable`. Feedback your team explicitly sends us (`reticle feedback`) is the only free text that ever leaves; it is never collected passively, is redacted client-side, and is disabled fleet-wide with `RETICLE_FEEDBACK=0`. |
59
+ | Does anything phone home? | **No app data unless you opt in.** Linking a project to the dashboard (`reticle connect`) sends its runs there; see [what is recorded](/what-is-recorded) and `reticle config` for what it sends. License checks are offline (Ed25519). The CLI sends opt-out usage metrics only (random id + event names, no code, no PII; on a licensed build they carry your licence id; see [telemetry](/telemetry)); disable fleet-wide with `RETICLE_TELEMETRY=0` or per-machine with `reticle telemetry disable`. Feedback your team explicitly sends us (`reticle feedback`) is the only free text that ever leaves; it is never collected passively, is redacted client-side, and is disabled fleet-wide with `RETICLE_FEEDBACK=0`. |
60
60
  | Where do artifacts live? | Your disk: `.reticle/runs/<id>.json` (atomic writes, bounded retention), `.reticle/flows/`, `.reticle/contract.json`. You own them. |
61
61
  | What can the server read? | The DOM/network/console/routing/state of the app under test, locally. |
62
62
  | Leak risk downstream? | The **`prod-preview` profile** redacts source `file:line`, raw bodies, and app-state values. |
@@ -13,7 +13,7 @@ Reticle works with none of these set. Each one changes a single behaviour, and m
13
13
  | `RETICLE_PORT` | daemon, CLI, build plugins | The bridge and daemon port. `.reticle.json`'s `port` sets it per project; this overrides it. |
14
14
  | `RETICLE_HOST` | daemon | The bridge bind host. Loopback by default. Anything else exposes the bridge beyond this machine, so it is opt-in. |
15
15
  | `RETICLE_TOKEN` | daemon | A shared secret the page must present when it connects. The pairing token covers this automatically; set it only for a hand-wired setup. |
16
- | `RETICLE_PAIRING_TOKEN_DIR` | daemon, `init`, build plugins | Where the auto-provisioned pairing token lives. Defaults to `~/.reticle`. Relocate it for CI or a read-only home directory. |
16
+ | `RETICLE_PAIRING_TOKEN_DIR` | daemon, `init`, `verify`, `try`, `open`, build plugins | Where the auto-provisioned pairing token lives. Defaults to `~/.reticle`. Move it for CI or a read-only home folder. Use an absolute path. A relative path is resolved against the folder the process runs in, and Reticle writes a warning. A Windows path such as `C:\Users\me\.reticle` on Linux, WSL or a container is not used: Reticle uses `~/.reticle` and writes a warning. |
17
17
  | `RETICLE_ALLOWED_ORIGINS` | daemon | Comma-separated WebSocket origins the bridge accepts. |
18
18
  | `RETICLE_STATE_DIR` | daemon, CLI | Daemon state: pid files, the discovery registry and logs. Defaults to `~/.reticle`. |
19
19
  | `RETICLE_DAEMON_READY_TIMEOUT_MS` | MCP proxy | How long the MCP proxy waits for a daemon to come up before reporting it unreachable. |
@@ -25,7 +25,7 @@ Reticle works with none of these set. Each one changes a single behaviour, and m
25
25
  | Variable | Read by | What it does |
26
26
  | --- | --- | --- |
27
27
  | `RETICLE_IDLE_SHUTDOWN_MS` | daemon | How long the daemon waits with no agent, no page and no lease before it exits. `0` keeps it running. |
28
- | `RETICLE_IDLE_ATTACHED_MS` | daemon | The same wait while an agent is attached, which is longer so a slow install or a thinking human is not cut off. Derived from the base when unset. |
28
+ | `RETICLE_IDLE_ATTACHED_MS` | daemon | The same wait while an agent is attached, which is longer so a slow install or a thinking human is not cut off. Derived from the base when unset. An agent that attached in the last 30 minutes counts as attached, if the daemon has served a tool call. |
29
29
  | `RETICLE_MCP_PROXY_IDLE_MS` | MCP proxy | How long an abandoned MCP stdio proxy waits before it exits. `0` disables the watcher. |
30
30
  | `RETICLE_JOURNAL` | daemon | Turns the causal journal off (`0`, `false`, `off`) or on. On by default. |
31
31
 
@@ -63,6 +63,8 @@ Reticle works with none of these set. Each one changes a single behaviour, and m
63
63
  | `RETICLE_API_KEY` | CLI, daemon | The API key minted on the platform. |
64
64
  | `RETICLE_CLOUD_KEY` | CLI, daemon | The key's earlier name. Still honoured. |
65
65
  | `RETICLE_CLOUD_URL`, `RETICLE_URL` | CLI, daemon | The platform host. `RETICLE_CLOUD_URL` wins when both are set. |
66
+ | `RETICLE_PROMPT_CAPTURE` | prompt hook | `0`, `false`, `off` or `no` stops the agent hooks on this machine from keeping anything: prompts, tool calls, the model and closing messages. `"promptCapture": false` in `.reticle.json` does the same for one project. See [prompt capture](/prompt-capture). |
67
+ | `RETICLE_BACKGROUND_SYNC` | daemon | `0`, `false`, `off` or `no` stops the daemon's background sync on this machine. `"backgroundSync": false` in `.reticle.json` does the same for one project. `reticle sync` still sends. |
66
68
  | `RETICLE_SYNC_INTERVAL_MS` | `reticle sync --watch` | How often the watching sync runs. Every 60 seconds by default. |
67
69
  | `RETICLE_HARNESS_MAX_STEPS` | autonomous drive | A hard ceiling on steps in one drive. It bounds cost. The drive itself runs on the platform and needs a linked project; each drive spends credits from the workspace's allowance. |
68
70
  | `RETICLE_DRIVE_ID` | autonomous drive | The free drive the platform granted, sent with every Harness call. `reticle try` sets it; you do not need to. |
@@ -82,5 +84,5 @@ Reticle works with none of these set. Each one changes a single behaviour, and m
82
84
  | `RETICLE_TELEMETRY_FILE` | CLI, daemon | Write telemetry to a local JSONL file and send nothing, so you can read exactly what would be sent. |
83
85
  | `RETICLE_TELEMETRY_URL`, `RETICLE_TELEMETRY_KEY` | CLI, daemon | Send telemetry to a different host or project, such as an EU region or your own instance. |
84
86
  | `RETICLE_FEEDBACK` | daemon | Turns off the feedback channel only. Usage telemetry follows `RETICLE_TELEMETRY`. |
85
- | `RETICLE_INSTALL_SOURCE` | `init`, installer, plugin | Names the published route an install came through (`docs_site`, `skill_file`, `plugin`). Self-declared, carries nothing about you, and changes nothing about the install. |
87
+ | `RETICLE_INSTALL_SOURCE` | `init`, installer, plugin | Names the published route an install came through: `skill_file`, `npx_skill`, `plugin`, `install_script`, `docs_site`, `readme`, `blog` or `cli_direct`. Any other value is sent as `unknown`. Self-declared, carries nothing about you, and changes nothing about the install. |
86
88
  | `RETICLE_TRACE` | daemon, CLI | Verbose internal tracing for people working on Reticle itself. Off by default. |
package/docs/faq.mdx CHANGED
@@ -5,14 +5,17 @@ description: Does it run in production, does it need React, does it replace Play
5
5
  icon: circle-question
6
6
  ---
7
7
 
8
- Reticle is a dev-only, localhost-only verification layer an AI coding agent uses to prove a change actually worked in your running app. It does not run in production, it does not need React, and it does not replace Playwright. The rest of the questions people actually ask are below.
8
+ Reticle is a verification layer that an AI coding agent uses to prove that a change worked in your running app. By default it runs only in development, on localhost. It does not need React, and it does not replace Playwright. The rest of the questions people actually ask are below.
9
9
 
10
10
  <AccordionGroup>
11
11
 
12
12
  <Accordion title="Does this run in production?">
13
- No, and it is built so that it cannot easily be made to. Keep `reticle.connect()` behind a dev
14
- guard; the SDK is side-effect-free and tree-shakes out of production builds. In a production `vite
15
- build`, the Vite plugin swaps the SDK for an inert stub and injects nothing.
13
+ Not by default. Keep `reticle.connect()` behind a dev guard. The SDK has no side effects and
14
+ tree-shakes out of production builds. In a production `vite build`, the Vite plugin replaces the
15
+ SDK with an inert stub and injects nothing.
16
+
17
+ To let an agent drive a staging or production deployment, use [production mode](/production-mode). It is opt-in. A visitor who did not activate it gets no SDK, no socket and no prompt.
18
+
16
19
  </Accordion>
17
20
 
18
21
  <Accordion title="Do I have to change my components?">
@@ -58,7 +61,7 @@ Reticle scopes to your project and prefers the active tab, and it **refuses rath
58
61
 
59
62
  </Accordion>
60
63
 
61
- <Accordion title="Why did I get `verified: \"unknown\"` on something that clearly worked?">
64
+ <Accordion title='Why did I get `verified: "unknown"` on something that clearly worked?'>
62
65
  Because Reticle could not confirm it saw the whole story, and it will not call that a pass.
63
66
 
64
67
  The most common cause is a **throttled tab**: a backgrounded browser tab has its timers suppressed, which suppresses the quiescence detection used to decide the page has settled. Focus the tab, or drive a dedicated context.
@@ -68,7 +71,7 @@ If you believe an `unknown` is wrong, report it. Reticle's own response says as
68
71
  </Accordion>
69
72
 
70
73
  <Accordion title="Does my data leave the machine?">
71
- Reticle is dev-only and localhost-only. Nothing from your app leaves the machine until you link a project (`reticle connect`, or `RETICLE_API_KEY` in CI). After that, what syncs is set by `reticle config`, and when the Harness drives, the platform sees the steps it drives. Secret values never leave. Credential-shaped values are redacted before they leave the page. You will see `[REDACTED]` in request bodies without configuring anything.
74
+ By default Reticle runs only in development, on localhost. Nothing from your app leaves the machine until you link a project (`reticle connect`, or `RETICLE_API_KEY` in CI). After that, what syncs is set by `reticle config`, and when the Harness drives, the platform sees the steps it drives. Secret values never leave. Credential-shaped values are redacted before they leave the page. You will see `[REDACTED]` in request bodies without configuring anything.
72
75
 
73
76
  Anonymous usage counts are sent by default, separately, and carry nothing from your app. `reticle telemetry disable`, `RETICLE_TELEMETRY=0` or `DO_NOT_TRACK=1` turns them off.
74
77
 
@@ -2,6 +2,7 @@
2
2
  title: Your first drive
3
3
  description: 'Clone to a real verdict in about fifteen minutes, and the traps that cost other people a debugging session.'
4
4
  icon: play
5
+ noindex: true
5
6
  ---
6
7
 
7
8
  The goal of this page is narrow and deliberate: **end with a real verdict on screen**, produced by Reticle against a running app, in a checkout you just cloned. Not a passing unit test, the thing the project exists to do.
@@ -57,9 +58,11 @@ node server/dist/command/cli.js verify http://localhost:4310/ --port 4400 \
57
58
  ```
58
59
  verified: yes
59
60
  reason: proved
60
- exit=0
61
+ { …the verdict as JSON… }
61
62
  ```
62
63
 
64
+ The command exits with code 0.
65
+
63
66
  That is the whole product in one line: a claim, checked against a running page, with an exit code you can put in CI. `verified: "yes"` is the only pass, `unknown` means Reticle could not tell, and it exits non-zero on purpose.
64
67
 
65
68
  Try making it fail, because a tool that only ever says yes is not telling you anything:
@@ -69,6 +72,13 @@ node server/dist/command/cli.js verify http://localhost:4310/ --port 4400 \
69
72
  --expect '{"kind":"text","contains":"definitely not on this page"}'
70
73
  ```
71
74
 
75
+ On Windows PowerShell, `npx.cmd` re-parses `--expect '{...}'` and strips the inner quotes. Prefer `--expect-file`:
76
+
77
+ ```powershell
78
+ '{"kind":"text","contains":"Saved"}' | Set-Content expect.json
79
+ npx @reticlehq/server verify http://localhost:3000 --expect-file expect.json
80
+ ```
81
+
72
82
  ## 3. Drive it properly, with the tools
73
83
 
74
84
  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:
@@ -89,23 +99,23 @@ node apps/e2e/specs/awkward-controls-test.mjs # a file upload, an unnamed b
89
99
  node apps/e2e/specs/auth-retry-not-a-defect-test.mjs # a 401 the app recovered from
90
100
  ```
91
101
 
92
- Both need step 1's processes running, and both need port 4400 free. See the traps below.
102
+ The specs start their own bridge on port 14400 (`RETICLE_PORT`, default in `apps/e2e/gate-harness.mjs`), so the bench app must dial 14400, not the step-1 daemon on 4400. To run every spec, use `pnpm test:e2e`. It boots the API, the bench app and next-smoke on the correct ports, then runs the battery. To run one spec by hand, stop the bench app from step 1 and start it again with `RETICLE_PORT=14400`. Then run the spec. Your own daemon on 4400 can keep running.
93
103
 
94
104
  ## The things that will bite you
95
105
 
96
- **Port 4400 is the bridge, and something is probably on it.** Every spec binds it. If your editor has an MCP client open, its daemon owns that port and a spec dies with `EADDRINUSE`. Do **not** `kill -9` the holder: on 4400 that list includes the `reticle mcp` proxy, and killing it cuts your own agent's link with no log, because the process that writes the log is the one that dies. Use the harness, which spares the proxy:
106
+ **The specs use port 14400, not 4400.** A daemon of your own on 4400 can keep running. If something holds 14400, a spec dies with `EADDRINUSE`. Do **not** `kill -9` whatever holds a Reticle port: on 4400 that can be the `reticle mcp` proxy, and killing it cuts your own agent's link with no log, because the process that writes the log is the one that dies. Use the harness, which spares the proxy:
97
107
 
98
108
  ```bash
99
- node -e "import('./apps/e2e/gate-harness.mjs').then(m=>m.freePortSafely(4400,{onNote:console.log}))"
109
+ node -e "import('./apps/e2e/gate-harness.mjs').then(m=>m.freePortSafely(14400,{onNote:console.log}))"
100
110
  ```
101
111
 
102
112
  **The bench app self-assigns its session id.** Matching on a fixed name waits forever. Specs identify it by the URL it serves from. Copy that pattern.
103
113
 
104
114
  **The bench app shows a login first, and views are reached from the nav.** A query parameter does not select a view. Log in, click the nav item, then drive.
105
115
 
106
- **Fixtures are in a second repo.** The apps in `apps/` are all already instrumented, so none of them can tell you whether a fresh install still works. That question lives in [`reticle-fixtures`](https://github.com/reticlehq/reticle-fixtures), which keeps a pristine `clean` branch of real third-party apps plus `main` and `reticle/<version>`. See [`fixtures.md`](https://github.com/reticlehq/reticle/blob/main/docs/fixtures.md).
116
+ **Fixtures are in a second repo.** The apps in `apps/` are all already instrumented, so none of them can tell you whether a fresh install still works. That question lives in `reticle-fixtures`, a private repository that the maintainers run. It keeps a pristine `clean` branch of real third-party apps plus `main` and `reticle/<version>`. See [Fixtures](/fixtures).
107
117
 
108
- **Telemetry fails silently.** Nothing throws, no test reddens, the data is just permanently gone. Read [`telemetry-contract.md`](https://github.com/reticlehq/reticle/blob/main/docs/telemetry-contract.md) before touching anything that emits.
118
+ **Telemetry fails silently.** Nothing throws, no test reddens, the data is just permanently gone. Read [`contributing/telemetry/contract.md`](https://github.com/reticlehq/reticle/blob/main/contributing/telemetry/contract.md) before touching anything that emits.
109
119
 
110
120
  **`core` is the contract and may not gain dependencies.** Anything crossing browser ↔ bridge ↔ agent is defined there as a constant plus a zod schema. Never inline a wire string elsewhere.
111
121
 
@@ -115,15 +125,15 @@ Run the fast gate before every commit. The others only when you touch what they
115
125
 
116
126
  | gate | when | measured |
117
127
  | --- | --- | --- |
118
- | `pnpm format:check && pnpm lint && pnpm typecheck && pnpm test:unit` | always | ~2 min |
128
+ | `pnpm verify` (`format:check`, `lint`, `typecheck`, `test:unit`) | always | ~2 min |
119
129
  | `pnpm test:e2e` | the tool surface, the wire contract, or an observer | ~8 min |
120
130
  | `pnpm test:e2e:desktop` | Electron, Tauri, the IPC observer, desktop capture | ~3 min, needs a Rust toolchain |
121
131
  | `pnpm gate:install` | `init`, `vite-plugin`, `next`, `babel-plugin` | ~15 min |
122
132
 
123
- `format:check` is first because CI enforces it and `pnpm lint` does not run it, a branch with all four heavy gates green locally can still turn CI red on formatting alone.
133
+ `pnpm verify` runs `format:check` first, because CI enforces formatting and `pnpm lint` does not check it. Without it, a branch that passes the other gates locally can still fail CI on formatting.
124
134
 
125
135
  ## Where to go next
126
136
 
127
137
  - [`CONTRIBUTING.md`](https://github.com/reticlehq/reticle/blob/main/CONTRIBUTING.md): the rules, the layout, and the PR flow
128
- - [`architecture.md`](architecture.md): how the pieces fit
138
+ - [Architecture](/architecture): how the pieces fit
129
139
  - Issues labelled **good first issue**. Each names the file to start in, how to reproduce, and how you will know you are done. If one is still too vague, say so on the issue; that feedback is worth more than a guess.
package/docs/flows.md CHANGED
@@ -6,8 +6,6 @@ icon: repeat
6
6
 
7
7
  A **flow** is a recorded interactive run that Reticle stores as JSON under `.reticle/flows/` and replays forever with no AI model in the loop. Steps are anchored on **meaning** (a testid, a signal, or an auto-derived component + source location), never on a `eXX` ref or a coordinate, so they survive refactors; when an anchor does drift, `reticle_flow_replay` names what changed and `reticle_verify { action: "heal" }` can rebind it.
8
8
 
9
- Reticle turns an interactive run into a **git-checked, replayable program** stored under `.reticle/`. Flows are anchored on **meaning** (testid + signal), not volatile element refs or coordinates, so they survive refactors, and when an anchor does drift, Reticle tells you _why_ and can repair it. This is what makes Reticle "the project's living test suite a human seeds and an agent maintains."
10
-
11
9
  > All of this is on-disk and human-readable, so flows are reviewed in PRs and diffed like code.
12
10
 
13
11
  ## The `.reticle/` directory
@@ -82,7 +80,7 @@ reticle_run({ tool: "reticle_flow_save", args: { flowName: "create-task" } })
82
80
  }
83
81
  ```
84
82
 
85
- Each step binds to a **semantic anchor**, never a `eXX` ref: a `testid`/`signal` when available, else an auto-derived `component` anchor (component name + source `file:line`) for an element with no testid, so the flow stays stable with zero hand-added testids. Only when none of those resolve is a step kept `degraded: true` (a last-resort "add a testid here" marker) rather than silently dropped.
83
+ Each step binds to a **semantic anchor**, never a `eXX` ref: a `testid`/`signal` when available, else an auto-derived `component` anchor (component name + source `file:line`) for an element with no testid, so the flow stays stable with zero hand-added testids. A press of a document key (Escape, Tab, a shortcut) with no target is aimed at no element, so it is saved with a `{ "kind": "role", "role": "document" }` anchor and replayed at the document, as it ran. Only when none of those resolve is a step kept `degraded: true` (a last-resort "add a testid here" marker) rather than silently dropped.
86
84
 
87
85
  ## Run a flow
88
86
 
@@ -106,6 +104,7 @@ There is no state to restore, so the steps before `from` run again quickly as se
106
104
  Every session's driving is saved as a flow at teardown when it declared a consequence. These flows:
107
105
 
108
106
  - are named from the `intent` you pass to `reticle_act_and_wait` (`drive-close-an-issue`), and carry it as the flow's `intent`;
107
+ - without an `intent`, carry a sentence built from what the steps expect, in order ("A POST request to /api/login is made, then the page moves to /home"), as `intent` with `intentDerived: true`, and are named from it (`drive-a-post-request-to-api-login-is-made-login`). A built sentence never enters the intent ledger, and the flow still reports that it declares no intent. Flows saved before this keep their names;
109
108
  - 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;
110
109
  - record, on every step, the `page` it ran on and the `endPage` it led to;
111
110
  - 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;
@@ -187,7 +186,7 @@ With `apply: false` the flow file is **never modified**; you get the proposed di
187
186
 
188
187
  ## Flows are your test suite
189
188
 
190
- `.reticle/` flows can be executed as CI specs via `@reticlehq/test`'s `flowsAsSpecs`, replayed with their `expect`/`success` predicates and skipping `dynamic` regions. See [Testing with Reticle](testing.md).
189
+ `.reticle/` flows can run as CI tests. `registerFlowSpecs('.reticle/flows', getSession)` from `@reticlehq/test` registers one vitest test for each flow. Each test replays the flow's `expect` and `success` predicates and skips its `dynamic` regions. To run the flows without vitest, call `flowsAsSpecs('.reticle/flows')`. It returns one spec for each flow, and you call `spec.run(session)` on each. See [Testing with Reticle](testing.md).
191
190
 
192
191
  ## Tool reference
193
192
 
@@ -32,7 +32,7 @@ Every Markdown page is prepended with two things before its content, so a single
32
32
  > Verdicts come from `reticle_act_and_wait`, `reticle_assert`, `reticle_act { steps }` when a step declares `expect`, and `reticle_verify`...
33
33
  > Package names are scoped `@reticlehq/*`. Run every CLI command as `npx @reticlehq/server <command>`...
34
34
 
35
- # reticle_query
35
+ # Getting started
36
36
  ...
37
37
  ```
38
38
 
@@ -233,28 +233,38 @@ What is still missing on Preact is the enrichment above the DOM: `reticle_inspec
233
233
 
234
234
  No config file to patch, so `connect()` goes in your entry module.
235
235
 
236
- ```ts
237
- // src/index.tsx
238
- import { reticle } from '@reticlehq/browser';
236
+ ```js
237
+ // src/index.js
239
238
  if (process.env.NODE_ENV !== 'production') {
240
239
  // The bridge refuses a connect with no pairing token, even on localhost.
241
- reticle.connect({ token: '<pairing token>' });
240
+ void Promise.all([
241
+ import('@reticlehq/browser'),
242
+ import('../reticle.local.js').catch(() => ({})),
243
+ ]).then(([{ reticle }, local]) => {
244
+ reticle.connect({ token: local.token });
245
+ });
242
246
  }
243
247
  ```
244
248
 
245
- The token is the contents of `~/.reticle/pairing-token`; do not commit it. On Create React App, `init` writes this for you: a dev-only connect module that reads the token from `REACT_APP_RETICLE_TOKEN`, which it puts in the gitignored `.env.development.local`. The same shape works for webpack, Parcel, and the Vue and Svelte CLIs.
249
+ `reticle init` writes this machine's pairing token to `reticle.local.js` at the project root and adds that file to `.gitignore`. Point the import at that file from your entry module. Do not commit it: each teammate runs `reticle init` for their own. The same shape works for webpack, Parcel, and the Vue and Svelte CLIs. On Create React App, `init` writes the connect for you: a dev-only connect module that reads the token from `REACT_APP_RETICLE_TOKEN`, which it puts in the git-ignored `.env.development.local`.
246
250
 
247
251
  ## Plain HTML
248
252
 
249
- 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:
253
+ 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, for you to paste. The snippet does not contain the token. It imports `reticle.local.js`, which `init` writes at the project root and adds to `.gitignore`. If your page is not served from the project root, change `./reticle.local.js` so that it points to that file:
250
254
 
251
255
  ```html
252
256
  <script type="module">
253
- import { reticle } from 'https://cdn.jsdelivr.net/npm/@reticlehq/browser@<version>/+esm';
254
- reticle.connect({ projectId: '<your-project-id>', token: '<pairing token>' });
257
+ Promise.all([
258
+ import('https://cdn.jsdelivr.net/npm/@reticlehq/browser@<version>/+esm'),
259
+ import('./reticle.local.js').catch(() => ({})),
260
+ ]).then(([{ reticle }, local]) =>
261
+ reticle.connect({ projectId: '<your-project-id>', token: local.token }),
262
+ );
255
263
  </script>
256
264
  ```
257
265
 
266
+ An Electron app with a static `index.html` is the exception. Its packaged renderer can also run on `localhost`, so `init` writes neither the snippet nor `reticle.local.js`, and gives a manual step instead. Connect only in development, for example only when `!app.isPackaged`, and see [Desktop apps](/desktop).
267
+
258
268
  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.
259
269
 
260
270
  ## Angular
@@ -282,14 +292,26 @@ Middleware is the Django-shaped answer: one place, runs for every page, no templ
282
292
  ```python
283
293
  # reticle_dev.py: add to MIDDLEWARE while DEBUG is on:
284
294
  # MIDDLEWARE = [..., "reticle_dev.ReticleDevMiddleware"]
295
+ import json
296
+ import os
297
+ from pathlib import Path
298
+
285
299
  from django.conf import settings
286
300
 
287
301
  _SNIPPET = """<script type="module">
288
302
  import { reticle } from 'https://cdn.jsdelivr.net/npm/@reticlehq/browser@<version>/+esm';
289
- reticle.connect({ projectId: '<your-project-id>', token: '<pairing token>' });
303
+ reticle.connect({ ...{ projectId: '<your-project-id>' }, token: __RETICLE_PAIRING_TOKEN__ });
290
304
  </script>"""
291
305
 
292
306
 
307
+ def _reticle_pairing_token():
308
+ folder = os.environ.get("RETICLE_PAIRING_TOKEN_DIR") or str(Path.home() / ".reticle")
309
+ try:
310
+ return (Path(folder) / "pairing-token").read_text().strip()
311
+ except OSError:
312
+ return ""
313
+
314
+
293
315
  class ReticleDevMiddleware:
294
316
  """Inject the Reticle SDK into HTML responses during development only."""
295
317
 
@@ -308,7 +330,8 @@ class ReticleDevMiddleware:
308
330
  body = response.content.decode(response.charset)
309
331
  if "</body>" not in body:
310
332
  return response
311
- response.content = body.replace("</body>", _SNIPPET + "</body>", 1).encode(
333
+ snippet = _SNIPPET.replace("__RETICLE_PAIRING_TOKEN__", json.dumps(_reticle_pairing_token()))
334
+ response.content = body.replace("</body>", snippet + "</body>", 1).encode(
312
335
  response.charset
313
336
  )
314
337
  if response.has_header("Content-Length"):
@@ -316,7 +339,7 @@ class ReticleDevMiddleware:
316
339
  return response
317
340
  ```
318
341
 
319
- `reticle init` emits this with your real version, project id and pairing token filled in. Copy it from there rather than from this page: the placeholders above are the three things that have to be right.
342
+ `reticle init` prints this with your real version and project id. Copy it from there, not from this page. The middleware reads this machine's pairing token from `~/.reticle/pairing-token` (or `RETICLE_PAIRING_TOKEN_DIR`) each time it serves a page, so the token is never in your code.
320
343
 
321
344
  Four details in that middleware are load-bearing, and each is there because the obvious version is wrong:
322
345