@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
@@ -15,12 +15,13 @@
15
15
  * URLs, no error messages — the accumulator has no way to hold them, which is a stronger guarantee
16
16
  * than a rule saying it shouldn't.
17
17
  */
18
- import { BrowserLaunchKind, coverageBucketOf, } from '@reticlehq/core/telemetry';
18
+ import { BrowserLaunchKind, } from '@reticlehq/core/telemetry';
19
19
  import { errorSkeleton, fingerprintError } from './error-fingerprint.js';
20
20
  import { describeToolParams } from './argument-shape.js';
21
21
  import { machineSnapshot } from './machine-snapshot.js';
22
22
  import { classifyError } from './error-class.js';
23
23
  import { HudMetrics } from './hud-metrics.js';
24
+ import { releaseVersionOf } from '../command/update/version-seen.js';
24
25
  /** Cap the distinct error shapes held in memory — a pathological loop must not grow unbounded. */
25
26
  const MAX_ERROR_KINDS = 40;
26
27
  /** Distinct tool names whose repeat-runs are tracked. Its own cap: the tool surface, not error shapes. */
@@ -143,7 +144,6 @@ export class SessionMetrics {
143
144
  /** Platform and Harness advisories shown to an agent this window, by kind. */
144
145
  #nudges = new Map();
145
146
  /** Harness drives refused for coverage this window, by coverage bucket. */
146
- #harnessRefused = new Map();
147
147
  /** Error buckets by whose defect, this window. */
148
148
  #errorClasses = new Map();
149
149
  /**
@@ -160,6 +160,13 @@ export class SessionMetrics {
160
160
  */
161
161
  #appConnects = 0;
162
162
  #firstAppAt;
163
+ /**
164
+ * Tool calls before the first app connected, session-lifetime like `#appConnects`. An agent that
165
+ * works against a daemon with no app sends no verdict and often no refusal; this gives it a shape.
166
+ */
167
+ #toolCallsBeforeApp = 0;
168
+ /** The SDK version of the last page that connected, session-lifetime like `#appConnects`. */
169
+ #sdkVersion;
163
170
  #clients = new Set();
164
171
  /** Client name -> version, session-lifetime. Never cleared: who drove is a fact about the session. */
165
172
  #clientVersions = new Map();
@@ -267,6 +274,9 @@ export class SessionMetrics {
267
274
  }
268
275
  this.#toolCalls += 1;
269
276
  this.#lifetimeToolCalls += 1;
277
+ if (this.#firstAppAt === undefined) {
278
+ this.#toolCallsBeforeApp += 1;
279
+ }
270
280
  bump(this.#toolCounts, tool);
271
281
  this.#inFlight = tool;
272
282
  this.#breadcrumb.push(tool);
@@ -390,8 +400,9 @@ export class SessionMetrics {
390
400
  * Zero of these at shutdown means the daemon ran and no app ever connected — a broken install.
391
401
  * Non-zero with no tool calls is the opposite problem. See SessionSummary.appConnects.
392
402
  */
393
- recordAppConnected() {
403
+ recordAppConnected(sdkVersion) {
394
404
  this.#appConnects += 1;
405
+ this.#sdkVersion = releaseVersionOf(sdkVersion) ?? this.#sdkVersion;
395
406
  this.#firstAppAt ??= this.#now();
396
407
  }
397
408
  /** One action driven at the page. Settled by the next verdict; see #unsettledActions. */
@@ -489,10 +500,6 @@ export class SessionMetrics {
489
500
  recordNudge(kind) {
490
501
  bump(this.#nudges, kind);
491
502
  }
492
- /** A Harness drive was refused because the app's coverage is below the gate. */
493
- recordHarnessRefused(percent) {
494
- bump(this.#harnessRefused, coverageBucketOf(percent));
495
- }
496
503
  /** Which tool surface was advertised to agents this session. */
497
504
  recordSurface(surface) {
498
505
  this.#surface = surface.slice(0, 32);
@@ -574,9 +581,6 @@ export class SessionMetrics {
574
581
  : {}),
575
582
  ...(this.#feedbackPrompted > 0 ? { feedbackPrompted: this.#feedbackPrompted } : {}),
576
583
  ...(this.#nudges.size > 0 ? { nudgesShown: Object.fromEntries(this.#nudges) } : {}),
577
- ...(this.#harnessRefused.size > 0
578
- ? { harnessRefusedCoverage: Object.fromEntries(this.#harnessRefused) }
579
- : {}),
580
584
  ...(this.#errorClasses.size > 0
581
585
  ? { errorClasses: Object.fromEntries(this.#errorClasses) }
582
586
  : {}),
@@ -588,6 +592,8 @@ export class SessionMetrics {
588
592
  ...(this.#firstAppAt === undefined
589
593
  ? {}
590
594
  : { msToFirstApp: Math.max(0, this.#firstAppAt - this.#startedAt) }),
595
+ ...(this.#sdkVersion === undefined ? {} : { sdkVersion: this.#sdkVersion }),
596
+ ...(this.#toolCallsBeforeApp > 0 ? { toolCallsBeforeApp: this.#toolCallsBeforeApp } : {}),
591
597
  ...this.#hud.summarize(),
592
598
  final,
593
599
  // Only on a real exit. A periodic flush carrying `exit: "unknown"` would read as a daemon that
@@ -649,7 +655,6 @@ export class SessionMetrics {
649
655
  0 === this.#postSocketFailures &&
650
656
  0 === this.#postRetriesSaved &&
651
657
  0 === this.#nudges.size &&
652
- 0 === this.#harnessRefused.size &&
653
658
  this.#hud.empty);
654
659
  }
655
660
  /** Zero the counters after a non-final flush so the next flush reports the NEXT window, not a total. */
@@ -680,7 +685,6 @@ export class SessionMetrics {
680
685
  this.#bugKinds.clear();
681
686
  this.#hud.reset();
682
687
  this.#nudges.clear();
683
- this.#harnessRefused.clear();
684
688
  // These five were never cleared, so every flush restated them and a sum over a session's
685
689
  // windows came out larger than its tool calls. Counts are windowed, all of them; only the
686
690
  // facts below (an app ever connected, which clients, which bug kinds seen) outlive a window.
@@ -1,4 +1,4 @@
1
- import { TelemetryEventKind, ProjectIdSource, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type OnboardingStep, type InstallSource, type McpConnection, type McpOutage, type ToolRefusal, type AppInstrumentation, type HarnessRun, type HudEvent, type PlatformLink, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core/telemetry';
1
+ import { TelemetryEventKind, ProjectIdSource, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type OnboardingStep, type InstallSource, type McpConnection, type McpOutage, type DaemonDeath, type ProxyExit, type WakeRefusal, type DaemonSpawnMethod, type ToolRefusal, type AppInstrumentation, type HarnessRun, type HudEvent, type PlatformLink, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core/telemetry';
2
2
  /**
3
3
  * Presence of this file marks the MACHINE as the Reticle team's own: every event carries
4
4
  * `internal: true`. The persistent twin of `RETICLE_INTERNAL=1`, for a dev machine where setting a
@@ -107,6 +107,14 @@ export interface TelemetryExtra {
107
107
  link?: PlatformLink;
108
108
  /** `harness_run`: one phase of one Harness run. */
109
109
  harness?: HarnessRun;
110
+ /** `mcp_proxy_exited`: why the proxy left, and what it saw in its life. */
111
+ proxyExit?: ProxyExit;
112
+ /** `daemon_died_unclean`: how old the dead daemon was when its pidfile was found. */
113
+ daemonDeath?: DaemonDeath;
114
+ /** `wake_refused`: what held the port. */
115
+ wakeRefusal?: WakeRefusal;
116
+ /** `daemon_started`: how the parent started the daemon. */
117
+ spawnMethod?: DaemonSpawnMethod;
110
118
  /**
111
119
  * `platform_linked`: the workspace just linked. Overrides the envelope's own reading, which looks
112
120
  * at the daemon's working directory and not at the project the HUD linked.
@@ -49,6 +49,12 @@ const POSTHOG_KEY = 'phc_q4yCxKMXHfWQ43VAz4HxJXZFLqFi4nBbg5v3avemggYo';
49
49
  * still key on distinct_id.
50
50
  */
51
51
  const POSTHOG_PERSONLESS = { $process_person_profile: false };
52
+ /**
53
+ * No address and no location leave with an event. `$geoip_disable` stops PostHog from looking up a
54
+ * city, a postal code and coordinates from the IP. PostHog also stores the request IP as `$ip` when
55
+ * the event leaves `$ip` empty (`null` counts as empty), so a fixed placeholder takes its place.
56
+ */
57
+ const POSTHOG_NO_IP = { $geoip_disable: true, $ip: '0.0.0.0' };
52
58
  /**
53
59
  * PostHog's group type for a licensed customer. Sent as `$groups` so PostHog can aggregate a whole
54
60
  * organisation natively: per-company retention, "which orgs went quiet", one row per customer rather
@@ -461,6 +467,11 @@ export const createTelemetry = (opts) => {
461
467
  ...(extra?.hud !== undefined ? { hud: extra.hud } : {}),
462
468
  ...(extra?.link !== undefined ? { link: extra.link } : {}),
463
469
  ...(extra?.harness !== undefined ? { harness: extra.harness } : {}),
470
+ ...(extra?.proxyExit !== undefined ? { proxyExit: extra.proxyExit } : {}),
471
+ ...(extra?.daemonDeath !== undefined ? { daemonDeath: extra.daemonDeath } : {}),
472
+ ...(extra?.wakeRefusal !== undefined ? { wakeRefusal: extra.wakeRefusal } : {}),
473
+ // A SCALAR: the event build and the wire schema, no `blocks` entry.
474
+ ...(extra?.spawnMethod !== undefined ? { spawnMethod: extra.spawnMethod } : {}),
464
475
  };
465
476
  // The wire schema's caps are only real if something applies them. An event that fails is
466
477
  // DROPPED, never sent and never repaired: the payload is exactly what the caps exist to stop.
@@ -473,7 +484,7 @@ export const createTelemetry = (opts) => {
473
484
  // PostHog filters and breakdowns operate on top-level properties, so a nested `feedback.stack`
474
485
  // would be invisible to the exact "which stacks report the most bugs" question this exists to
475
486
  // answer. One prefix keeps them grouped in the UI without a nested path.
476
- const { anonymousId: distinctId, event: name, ts, sessionId: eventSessionId, feedback, session, project, verification, versionChange, crash, identity, connection, init, onboarding, bug, refusal, outage, instrumentation, hud, link, harness, ...rest } = event;
487
+ const { anonymousId: distinctId, event: name, ts, sessionId: eventSessionId, feedback, session, project, verification, versionChange, crash, identity, connection, init, onboarding, bug, refusal, outage, instrumentation, hud, link, harness, proxyExit, daemonDeath, wakeRefusal, ...rest } = event;
477
488
  // `$session_id` is PostHog's OWN session property, so sending ours under that name lights up
478
489
  // its built-in session views and funnels for free rather than requiring custom HogQL everywhere.
479
490
  // `sessionId` rides only on events that happened inside a daemon run. A one-shot CLI command
@@ -508,6 +519,9 @@ export const createTelemetry = (opts) => {
508
519
  hud,
509
520
  link,
510
521
  harness,
522
+ proxyExit,
523
+ daemonDeath,
524
+ wakeRefusal,
511
525
  };
512
526
  for (const [prefix, block] of Object.entries(blocks)) {
513
527
  for (const [key, value] of Object.entries(block ?? {})) {
@@ -521,6 +535,7 @@ export const createTelemetry = (opts) => {
521
535
  properties: {
522
536
  ...properties,
523
537
  ...POSTHOG_PERSONLESS,
538
+ ...POSTHOG_NO_IP,
524
539
  // Only on a licensed build. An unlicensed event has no organisation to belong to, and an
525
540
  // empty group key would mint a phantom "no org" bucket that every OSS install falls into.
526
541
  ...(event.licenseId !== undefined
@@ -19,4 +19,6 @@ import { type BrowserBrand, type Verification } from '@reticlehq/core';
19
19
  */
20
20
  export declare function verificationOf(toolName: string, result: Record<string, unknown>, durationMs: number, brand?: BrowserBrand,
21
21
  /** The call ran inside a Harness drive's async context (`currentDrivenBy()`), read by the caller. */
22
- drivenByHarness?: boolean): Verification | undefined;
22
+ drivenByHarness?: boolean,
23
+ /** The page SDK version the session announced on HELLO. */
24
+ pageSdkVersion?: string): Verification | undefined;
@@ -13,6 +13,7 @@
13
13
  import { CaptureLoss, ReplayStatus, ReticleTool, VerificationDriver, Verified, VerifiedReason, } from '@reticlehq/core';
14
14
  import { VERDICT_TOOLS } from '../surface/tools/feedback-tools.js';
15
15
  import { getBrowserMode } from './browser-mode.js';
16
+ import { releaseVersionOf } from '../command/update/version-seen.js';
16
17
  /**
17
18
  * The deciding clause, read off the result `decideVerified` wrote it onto.
18
19
  *
@@ -116,7 +117,9 @@ function passedOf(result, status) {
116
117
  */
117
118
  export function verificationOf(toolName, result, durationMs, brand,
118
119
  /** The call ran inside a Harness drive's async context (`currentDrivenBy()`), read by the caller. */
119
- drivenByHarness = false) {
120
+ drivenByHarness = false,
121
+ /** The page SDK version the session announced on HELLO. */
122
+ pageSdkVersion) {
120
123
  if (!VERDICT_TOOLS.has(toolName))
121
124
  return undefined;
122
125
  // A paused session REFUSES the call — nothing driven, nothing asserted. The refusal carries a
@@ -141,6 +144,7 @@ drivenByHarness = false) {
141
144
  return undefined;
142
145
  const reason = reasonOf(result);
143
146
  const uncleanLoss = uncleanLossOf(result, reason);
147
+ const sdkVersion = releaseVersionOf(pageSdkVersion);
144
148
  return {
145
149
  via: toolName,
146
150
  verified: verified ?? (true === passed ? 'yes' : 'no'),
@@ -179,5 +183,7 @@ drivenByHarness = false) {
179
183
  // agent keeps calling tools, so a process-wide flag credited the agent's verdicts too. Absent is
180
184
  // the ordinary case — the agent called the tool itself.
181
185
  ...(drivenByHarness ? { driven: VerificationDriver.HARNESS } : {}),
186
+ // WHICH page SDK the verdict was judged on. A page older than the daemon misses newer fixes.
187
+ ...(sdkVersion === undefined ? {} : { sdkVersion }),
182
188
  };
183
189
  }
@@ -14,6 +14,7 @@ import { originOf } from './portal/session/session-manager.js';
14
14
  import { Bridge } from './portal/bridge/bridge.js';
15
15
  import { FlowStore } from './language/flows/flows.js';
16
16
  import { makeJournalAttach } from './memory/journal/attach-journal.js';
17
+ import { setEpisodeLogEnabled } from './memory/journal/episode-log.js';
17
18
  import { endLiveSessions, makeSessionEnd, recordDriveRun } from './memory/journal/session-end.js';
18
19
  import { attachDriveRunFlush } from './memory/journal/drive-run-flush.js';
19
20
  import { AmbientStore } from './memory/journal/ambient-store.js';
@@ -34,13 +35,15 @@ import { readBackgroundTraffic } from './portal/session/facts/background-traffic
34
35
  */
35
36
  export function attachJournal(bridge, deps) {
36
37
  const journalAttach = makeJournalAttach(deps);
38
+ // The episode log is part of the journal: the same switch turns both off.
39
+ setEpisodeLogEnabled(deps.enabled);
37
40
  const ambientStore = new AmbientStore(deps.fs, deps.reticleRoot);
38
41
  // Built once, not per session: the resolver walks config discovery and the user-level registry,
39
42
  // and neither changes between two tabs connecting a second apart.
40
43
  const resolveArtifactRoot = artifactRootResolver(deps.reticleRoot, {
41
44
  servingDirectory: servingDirectoryOf,
42
45
  });
43
- bridge.attachSessionCreate((session) => {
46
+ const stampProject = (session) => {
44
47
  // Stamp the project's own `.reticle` before ANY counter fires for this session. Without it every
45
48
  // verdict is recorded against wherever the daemon was started, which is how one app's evidence
46
49
  // reached a different account's production dashboard.
@@ -58,9 +61,13 @@ export function attachJournal(bridge, deps) {
58
61
  // repository nor the root we were about to write into: it created `.reticle/` — holding nothing
59
62
  // but the ignore file — wherever the daemon was launched, coming back every boot after the user
60
63
  // deleted it, while the journals this ignore protects landed in another tree, uncovered.
61
- if (deps.enabled)
64
+ if (deps.enabled) {
62
65
  void ensureWorkspaceGitignore(deps.fs, session.artifactRoot);
66
+ }
63
67
  journalAttach(session);
68
+ };
69
+ bridge.attachSessionCreate((session) => {
70
+ stampProject(session);
64
71
  // Seed the learned ambient map so a fresh session starts knowing which regions churn, instead of
65
72
  // re-learning from zero. Best-effort + async: a late seed still helps, a failure is silent.
66
73
  if (deps.enabled) {
@@ -70,6 +77,9 @@ export function attachJournal(bridge, deps) {
70
77
  .catch(() => undefined);
71
78
  }
72
79
  });
80
+ // A page that connected without a project and names one later (an app's own connect() ran before
81
+ // the build plugin's) is stamped again, so its evidence leaves the unmatched bucket from now on.
82
+ bridge.attachProjectAdopted(stampProject);
73
83
  // Teardown: flush the journal tail to disk + persist what this session learned.
74
84
  const teardown = makeSessionEnd({
75
85
  ...deps,
package/docs/README.md CHANGED
@@ -69,11 +69,13 @@ Everything here is published to [docs.reticle.sh](https://docs.reticle.sh) by `d
69
69
  | [multi-agent-testing.md](multi-agent-testing.md) | more than one agent on one app |
70
70
  | [human-control.md](human-control.md) | taking the wheel back from the agent |
71
71
  | [deploy-checks.md](deploy-checks.md) | running Reticle against a deployed build |
72
+ | [production-mode.md](production-mode.md) | driving a live site under a policy that refuses payments, deletions and invites |
72
73
  | [token-efficiency.md](token-efficiency.md) | why it costs less than a screenshot loop |
73
74
  | [benchmarks.md](benchmarks.md) | how the numbers were measured, including where Reticle loses |
74
75
  | [vs-playwright-mcp.mdx](vs-playwright-mcp.mdx) | outside-in vs inside-out, and when Playwright is right |
75
76
  | [vs-chrome-devtools-mcp.mdx](vs-chrome-devtools-mcp.mdx) | cheaper per look, catches less: the trade, measured |
76
77
  | [vs-screenshots.mdx](vs-screenshots.mdx) | why a better vision model does not fix a non-visual bug |
78
+ | [prompt-capture.md](prompt-capture.md) | the prompt hook `init` installs: what it keeps, what leaves, and every switch to turn it off |
77
79
  | [what-is-recorded.md](what-is-recorded.md) | what Reticle keeps, what is redacted, and what it takes for any of it to leave your machine |
78
80
  | [telemetry.md](telemetry.md) | what is collected, and how to turn it off |
79
81
  | [local-registry.md](https://github.com/reticlehq/reticle/blob/main/docs/local-registry.md) | installing an unpublished build |
@@ -92,8 +94,8 @@ Everything here is published to [docs.reticle.sh](https://docs.reticle.sh) by `d
92
94
  | [adapters.md](adapters.md) | what an adapter is, the three kinds, and how to write one |
93
95
  | [state-files.md](state-files.md) | what to do when a file on disk came from a different version of Reticle |
94
96
  | [system-map.md](system-map.md) | how a tool call reaches the app, and which failures are silent |
95
- | [telemetry-contract.md](https://github.com/reticlehq/reticle/blob/main/docs/telemetry-contract.md) | required reading before touching anything that emits |
96
- | [telemetry-events.mdx](https://github.com/reticlehq/reticle/blob/main/docs/telemetry-events.mdx) | every event kind and its payload, and the question each one exists to answer |
97
+ | [contributing/telemetry/contract.md](https://github.com/reticlehq/reticle/blob/main/contributing/telemetry/contract.md) | required reading before touching anything that emits |
98
+ | [contributing/telemetry/events.md](https://github.com/reticlehq/reticle/blob/main/contributing/telemetry/events.md) | every event kind and its payload, when it is sent, why, and an example |
97
99
  | [first-drive.md](first-drive.md) | clone to a real verdict in about fifteen minutes, and the traps on the way |
98
100
  | [debugging.md](debugging.md) | debugging Reticle itself: the four signals and what each answers |
99
101
  | [fixtures.md](https://github.com/reticlehq/reticle/blob/main/docs/fixtures.md) | the sibling `reticle-fixtures` repo: install complexity, not regressions |
package/docs/actions.mdx CHANGED
@@ -5,7 +5,7 @@ description: Every action reticle_act and reticle_act_and_wait can perform, with
5
5
  icon: hand-pointer
6
6
  ---
7
7
 
8
- Every action Reticle can perform takes the same shape: a `ref` from [`reticle_look { action: "page" }`](/tools/snapshot) or [`reticle_look { action: "find" }`](/tools/query), an `action` name, and an optional `args` object. The same actions are available on [`reticle_act`](/tools/act), which proves nothing, and on [`reticle_act_and_wait`](/tools/act-and-wait), which returns a verdict.
8
+ Every action Reticle can perform takes the same shape: an element, an `action` name, and an optional `args` object. Give the element as a `ref` from [`reticle_look { action: "page" }`](/tools/snapshot) or [`reticle_look { action: "find" }`](/tools/query). Or give a `target` (`{ testid }`, `{ text }`, `{ role, name }` or `{ label }`) that Reticle finds in the same call. A press of Escape, Tab or a modifier shortcut goes to the document and needs neither. The same actions are available on [`reticle_act`](/tools/act), which proves nothing, and on [`reticle_act_and_wait`](/tools/act-and-wait), which returns a verdict.
9
9
 
10
10
  ```json
11
11
  { "ref": "e104", "action": "fill", "args": { "value": "reticle" } }
@@ -13,7 +13,7 @@ Every action Reticle can perform takes the same shape: a `ref` from [`reticle_lo
13
13
 
14
14
  | Action | Arguments | Notes |
15
15
  | --- | --- | --- |
16
- | `click` / `dblclick` | none | Dispatches a real click |
16
+ | `click` / `dblclick` | none | Dispatches the full pointer and mouse sequence on the element. `args.native: true` forces a trusted native click |
17
17
  | `hover` | none | `mouseover` + `mouseenter`, so JS hover state triggers |
18
18
  | `focus` / `blur` | none | |
19
19
  | `fill` | `{ value }` | Sets value via a React-safe native setter, then `input` and `change` |
@@ -53,7 +53,7 @@ Reticle blocks a control that looks destructive before acting on it:
53
53
  ```json
54
54
  {
55
55
  "error": "potentially destructive action blocked; retry with args.confirmDangerous=true",
56
- "recovery": "Reticle blocked a potentially destructive control (delete, remove, revoke…) on purpose. If you mean it, retry the same action with args.confirmDangerous set to true. This is a deliberate refusal, not a defect: there is nothing to report."
56
+ "recovery": "Reticle blocked a control whose label reads as consequential (delete, remove, revoke, pay…) on purpose. If you mean it, send the SAME call again with `args: { confirmDangerous: true }`: it goes inside the `args` object, not beside it. This is a deliberate refusal, not a defect: there is nothing to report."
57
57
  }
58
58
  ```
59
59
 
@@ -96,13 +96,14 @@ Several actions in one round trip:
96
96
 
97
97
  <Warning>
98
98
  [`reticle_act { steps: [...] }`](/tools/act-sequence) returns `ok: true` when the steps were
99
- **dispatched**, not when they worked. Batch the setup, then prove the outcome with
100
- [`reticle_act_and_wait`](/tools/act-and-wait) on the final step.
99
+ **dispatched**, not when they worked. A step without `expect` proves nothing. Give the last step
100
+ an `expect` that names the end state, for example `"expect": { "kind": "signal", "name": "auth:granted" }`.
101
+ The sequence then returns a verdict and stops at the first `expect` that does not hold.
101
102
  </Warning>
102
103
 
103
104
  ## Real versus synthetic input
104
105
 
105
- `inputMode` in the response says which you got. `synthetic` means events dispatched programmatically. Correct for the overwhelming majority of apps. `real` means native CDP input, available under `reticle drive`, and needed by a few drag-and-drop and pointer-gesture libraries.
106
+ `inputMode` in the response says which you got. `synthetic` means events dispatched programmatically. This is correct for most apps. `real` means native CDP input. You get it under `reticle drive`, and a few drag-and-drop and pointer-gesture libraries need it. On a leased tab (`reticle_lease`), a `press` uses the real keyboard: Tab moves focus, and a press with a `ref` focuses that element first, so Enter or Space presses it as a person would.
106
107
 
107
108
  <Warning>
108
109
  On a throttled tab, synthetic timers and pointer gestures can silently no-op. Pass
package/docs/adapters.md CHANGED
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  title: Writing an adapter
3
3
  description: What an adapter is, which kinds exist, and how to build one of your own.
4
+ noindex: true
4
5
  ---
5
6
 
6
7
  Reticle watches an app from the inside. Most of what it does is the same everywhere: a click is a click, a failed request is a failed request. A few things are not, and those are what adapters are for.
@@ -13,7 +14,7 @@ There are three kinds, and they answer three different questions.
13
14
  | **build** | how does the SDK get into the page, and how does an element learn which file it came from? | `@reticlehq/vite-plugin` |
14
15
  | **realm** | how do we watch this kind of app's own traffic and take its picture? | `@reticlehq/electron` |
15
16
 
16
- You almost certainly want the first one. It is the only kind you can write today without changing Reticle itself, and it is the one that makes a failing check say `src/CartRow.tsx:42` instead of "something on the page".
17
+ You almost certainly want the first one. It is one of two kinds you can write today without a change to Reticle itself (the other is a lint rule, see the end of this page). It is the kind that makes a failing check say `src/CartRow.tsx:42` instead of "something on the page".
17
18
 
18
19
  ## Writing a framework adapter
19
20
 
@@ -6,7 +6,7 @@ icon: bolt
6
6
 
7
7
  The loop is **look → act → observe → assert**. Verdicts come from `reticle_act_and_wait`, `reticle_assert`, `reticle_act { steps }` when a step declares `expect`, and `reticle_verify` (`change`/`flows`). Everything else moves or reads the app and proves nothing. Only `verified: "yes"` is a pass: `unknown` means Reticle could not tell, and `no-fault` means nothing was declared to prove. Reach for `reticle_act_and_wait({ ref, action, until })` first: it names the expected consequence before the action, which is the difference between a check and a rationalisation.
8
8
 
9
- One screen to get fluent. Reticle is the **proof layer for AI agents**: no screenshots, no vision model, evidence not prose. Everything below returns structured data. Full guide: [usage.md](usage.md).
9
+ One screen to get fluent. Reticle is the **proof layer for AI agents**: no screenshots, no vision model, evidence not prose. Everything below returns structured data. Full guide: [Usage](/usage).
10
10
 
11
11
  ## The core loop: look → act → observe → assert
12
12
 
@@ -48,6 +48,7 @@ Two consequences worth knowing:
48
48
 
49
49
  - **You can assert what a field contains.** Do not read the value with `reticle_look { action: "find" }` and compare it in prose: that produces no verdict and does not count as verification.
50
50
  - **`{ role, text }` checks the text,** so it does not match every button with that role.
51
+ - **A `text` predicate reads rendered text, not a field's value.** Text typed into an `<input>` or a `<textarea>` is not on the page as text, so `{ kind: "text", contains }` does not see it. Assert it with `value` on an `element` predicate, as above.
51
52
 
52
53
  Fields that nothing can check are refused rather than ignored: `by` without a `value`, and `label`, `placeholder`, `testid` or `component` when a higher-precedence field already selected the element. `alt` is the one that can still be checked beside another locator: on an image it is compared with the image's `alt` attribute (so `alt: ""` passes on a decorative image but not on one missing its alt), and on anything that is not an image it is refused, with a hint to use `name`. An element query is a first-match dispatch, not a conjunction.
53
54
 
@@ -121,7 +122,7 @@ Most of what used to be a tool of its own is now an action on one of them:
121
122
 
122
123
  Call an old name and you are told the new one. `reticle_tools` prints the live surface and every mapping.
123
124
 
124
- - `reticle_verify`: **the only core tool that CONCLUDES.** `{ action:"crawl" }` drives every reachable control itself and reports the whole fault set in one call (destructive: it really clicks). `{ action:"flows" }` replays every saved flow with no model in the loop. Reach for it when you think you are done: every other tool here answers "here is more to look at".
125
+ - `reticle_verify`: **the tool that checks the whole app or the whole change in one call.** `{ action:"crawl" }` drives every reachable control itself and reports the whole fault set in one call (destructive: it really clicks). `{ action:"flows" }` replays every saved flow with no model in the loop. Reach for it when you think you are done: every other tool here answers "here is more to look at".
125
126
 
126
127
  Not on this surface at all, but one hop away: `reticle_run { tool, args }` calls any registered tool by name, advertised or not, and `reticle_tools { names: [...] }` loads its arguments. That reaches `reticle_capabilities` (the app's whole testable surface in one call), `reticle_domain` (learn the app + gaps), `reticle_run { tool: "reticle_baseline", args: { action: "diff" } }`, `reticle_project` (run history), `reticle_screenshot`, `reticle_visual_diff`, `reticle_storage`, `reticle_network_mock` and `reticle_clock`. A daemon started with `RETICLE_ADVERTISE_ALL_TOOLS=1` advertises some of these directly (`reticle_capabilities`, `reticle_screenshot`, `reticle_visual_diff`, `reticle_storage`, `reticle_network_mock`, `reticle_clock`, plus `reticle_record`, `reticle_flow_save`, `reticle_flow_replay`, `reticle_intent` and `reticle_context`); the rest stay behind `reticle_run`. It also switches to the unmerged spelling: `reticle_snapshot`/`reticle_query`/`reticle_inspect`/`reticle_state` replace `reticle_look`, `reticle_network`/`reticle_console` replace `reticle_observe { action }`, `reticle_wait_for` replaces `reticle_assert { action: "wait" }`, `reticle_sessions`/`reticle_feedback` replace `reticle_session` list/feedback, and `reticle_act_sequence` replaces `reticle_act { steps }`. Examples on this page use the default spelling.
127
128
 
@@ -151,6 +152,8 @@ Recent additions, each of which answers a question agents were previously asking
151
152
  | `elided` | `reticle_act_and_wait` | Some diff arrays were capped. The count is real even when the list is trimmed, so a small array does not mean a quiet app. |
152
153
  | `colorTokens`, `themeScope` | `reticle_look { action: "element" }` | Every design token matching a colour, not one arbitrary winner, and which theme the reading was taken under. The singular `colorToken` is `null` when several tokens share a colour, because naming one of them was the defect. |
153
154
  | `why` | `reticle_session { action: "list" }`, when the list is empty | Why nothing is connected, and the next action. An empty list is never the end of the road. |
155
+ | `engineBlindSpots` | `reticle_act_and_wait`, `reticle_assert` | What the engine could not see in this run: `pixels-unobserved` once an action ran with a `<canvas>` on screen, `claim-arithmetic-unchecked` while a claim states an amount and no verdict ran a `compare`. A clean verdict says nothing about these. |
156
+ | `sdkVersion`, `sdkBehind` | `reticle_session { action: "list" }` | The Reticle SDK version in each tab. `sdkBehind` is present when the page is a minor release or more behind the daemon, and gives the update command. Each verdict on such a page carries a short `sdk_behind` note. |
154
157
 
155
158
  **When a verdict comes back `unsettled`,** it now names what it was waiting for and what the window actually held. Read those before retrying: an `unknown` that explains itself is a retry with a better `until`, and one that does not is a dead end.
156
159
 
@@ -158,15 +161,26 @@ Recent additions, each of which answers a question agents were previously asking
158
161
 
159
162
  ## Start here
160
163
 
161
- 1. **First move, always:** `reticle_session { action: "list" }` finds the connected tab. Just ran `reticle init` or restarted the dev server? You do not need to poll: live tools wait briefly for a tab that is about to connect. Omit `sessionId` if there's only one tab. **An empty list is not a dead end: read the `why` field.** It names which case this is (no app running, an app running that has never dialled this daemon, a project that never went through `init`, or a tab that closed) and the fix for each. Do not fall back to static reasoning until you have read it.
164
+ 1. **First move, always:** `reticle_session { action: "list" }` finds the connected tab. Just ran `reticle init` or restarted the dev server? You do not need to poll: live tools wait briefly for a tab that is about to connect. Omit `sessionId` if there's only one tab. **An empty list is not a dead end: read `why`.** It names the case (no app running, an app that never dialled this daemon, a project that is not wired, or a tab that closed) and the fix for each. In a project that is not wired, `next_step` also names the next calls. `lease` (present when an app is running) is the zero-install check of the running app: do it first, because it changes no file. `consent` is the question for `init`, which changes `package.json`, the lockfile and the build config: ask the user, and run `init` only if they agree. Do not fall back to static reasoning until you have read `why`.
162
165
  2. `reticle_run({ tool: "reticle_domain", args: {} })` learns the app BEFORE testing: the saved flows, what each asserts, and the **gaps** (declared signals/testids that no flow verifies, i.e. untested intent). Tells you what to test and where the real risk is without crawling the whole app. Falls back to `reticle_run({ tool: "reticle_capabilities", args: {} })` for the raw testable surface (`testids`, `signals`, `stores`, `flows`).
163
166
  3. Run the loop: **look → act → observe → assert**, cross-checking the 4 layers on anything that matters.
164
167
 
168
+ ## No MCP tools in this host
169
+
170
+ A cloud agent or a worker can have a shell and no MCP tools. Use the CLI. `verify --expect` asks a running daemon and never starts one, so start it first:
171
+
172
+ ```bash
173
+ npx @reticlehq/server serve --headless
174
+ npx @reticlehq/server verify http://localhost:5173/ --expect '{"kind":"text","contains":"Welcome"}'
175
+ ```
176
+
177
+ It exits `0` only on `verified: "yes"`. `--expect-file <path>` reads the predicate from a file, and `--timeout N` sets how long it waits (default `4000` ms). To move a connected tab to another page first, run `npx @reticlehq/server open <url> --navigate`. For an action that `--expect` cannot do, use the [HTTP MCP transport](/http-transport).
178
+
165
179
  ## Token note
166
180
 
167
- - **Keep observation cheap.** Prefer `reticle_look { action: "find" }` / scoped or `interactive` `reticle_look { action: "page" }` / `reticle_assert` over dumping the full tree. A full verify loop is ~100 tokens; see [token-efficiency.md](token-efficiency.md) (~73× leaner than full-tree snapshots).
181
+ - **Keep observation cheap.** Prefer `reticle_look { action: "find" }` / scoped or `interactive` `reticle_look { action: "page" }` / `reticle_assert` over dumping the full tree. A full verify loop is ~100 tokens; see [Token efficiency](/token-efficiency) (~73× leaner than full-tree snapshots).
168
182
  - **Re-look with `reticle_look({ action: "page", diff:true })`** after an action; it returns only what changed (`mode:delta`/`unchanged`), ~99% fewer tokens than a full re-snapshot and no stale tree to mis-read. Every snapshot/query result carries `cost:{ bytes, tokens }`, so re-scope before reading if it's large.
169
183
  - **Cap broad reads.** `reticle_look { action: "find" }` takes `limit` (caps descriptors; reports `total`/`truncated`) and `count_only` (just the match count). `reticle_observe { action: "network" }` / `reticle_observe { action: "console" }` take `limit` (most-recent-N, reports `droppedOldest`) and carry the same `cost` hint, so a busy page or wide window never floods your context unnoticed.
170
184
  - **A saved flow tells you if it's a real test.** `reticle_flow_save` returns `assertions.grade` (`asserted` / `presence-only` / `assertion-free`); if it's not `asserted`, add a consequence (`reticle_annotate` assert-signal/assert-net or a success-state) so it can't pass while broken. On replay, an ambiguous heal (two testids tie) is surfaced, never auto-applied. And an `apply` heal re-replays the rebound flow and **refuses to write** if the success consequence no longer fires (`status:consequence_broken`): it heals the locator, never the intent.
171
185
  - **Predicate schema is not bloated.** The recursive predicate DSL used by `reticle_assert` / `reticle_assert { action: "wait" }` / `reticle_act_and_wait` is defined once (`PredicateSchema` in `@reticlehq/core`) and shared by all three tools. No action needed.
172
- - **One tool surface, and a hatch behind it.** Reticle advertises navigate/act/act_and_wait/assert/look/observe/session/verify, plus `reticle_tools` and `reticle_run`. That is the whole detect loop, the file-pointer, the feedback channel, the handback, and the one tool that CONCLUDES, reached by an `action` argument rather than by a name each. `reticle_tools` discovers: no args lists every registered tool name and summary, `names:[…]` loads full params on demand. `reticle_run { tool, args }` then calls any of them, advertised or not, which is what keeps recording a flow, screenshots, visual diff, storage, network mocking and the clock reachable without spending a slot on each. A daemon started with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read at startup, so restart it) advertises a wider set outright (the default tools plus the extra ones listed above), with output schemas, under the unmerged names listed above. It is not the whole registry: the count is capped because editors budget tools across every connected MCP server. `reticle_verify` is advertised: `{ action:"flows" }` replays every saved flow with no model in the loop, and `{ action:"crawl" }` drives every reachable control itself and reports the whole fault set in one call. **Sizes are deliberately not quoted here.** A count in prose goes stale, and has three times already. `reticle_tools` reports the live surface, and SKILL.md carries the one gated table.
186
+ - **One tool surface, and a hatch behind it.** Reticle advertises navigate/act/act_and_wait/assert/look/observe/session/verify, plus `reticle_tools` and `reticle_run`. That is the whole detect loop, the file-pointer, the feedback channel, the handback, and the tool that checks the whole app in one call, reached by an `action` argument rather than by a name each. `reticle_tools` discovers: no args lists every registered tool name and summary, `names:[…]` loads full params on demand. `reticle_run { tool, args }` then calls any of them, advertised or not, which is what keeps recording a flow, screenshots, visual diff, storage, network mocking and the clock reachable without spending a slot on each. A daemon started with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read at startup, so restart it) advertises a wider set outright (the default tools plus the extra ones listed above), with output schemas, under the unmerged names listed above. It is not the whole registry: the count is capped because editors budget tools across every connected MCP server. `reticle_verify` is advertised: `{ action:"flows" }` replays every saved flow with no model in the loop, and `{ action:"crawl" }` drives every reachable control itself and reports the whole fault set in one call. **Sizes are deliberately not quoted here.** A count in prose goes stale, and has three times already. `reticle_tools` reports the live surface, and SKILL.md carries the one gated table.
@@ -4,9 +4,9 @@ description: 'The moving parts, the data flow between them, and the design decis
4
4
  icon: sitemap
5
5
  ---
6
6
 
7
- Reticle has three moving parts: a dev-only **SDK** inside your page that instruments the DOM, network, console, routing and framework state; a local **server** that runs a WebSocket bridge to that SDK and an MCP server for your agent; and a git-checked **`.reticle/` workspace** holding flows, baselines and run artifacts. Your agent calls an MCP tool, the server turns it into a command over the WebSocket, the SDK runs it in the real page and streams structured events back.
7
+ Reticle has three moving parts: a development **SDK** inside your page that instruments the DOM, network, console, routing and framework state; a local **server** that runs a WebSocket bridge to that SDK and an MCP server for your agent; and a git-checked **`.reticle/` workspace** holding flows, baselines and run artifacts. Your agent calls an MCP tool, the server turns it into a command over the WebSocket, the SDK runs it in the real page and streams structured events back.
8
8
 
9
- > For engineers evaluating Reticle, integrating it at scale, or contributing. It explains the moving parts, the data flow, and the design decisions behind them. If you just want to get running, start with [getting-started.md](getting-started.md); come back here when you want to know _why_.
9
+ > For engineers evaluating Reticle, integrating it at scale, or contributing. It explains the moving parts, the data flow, and the design decisions behind them. If you just want to get running, start with [Getting started](/getting-started); come back here when you want to know _why_.
10
10
 
11
11
  ---
12
12
 
@@ -38,6 +38,9 @@ Reticle is a pnpm + Turborepo monorepo. The split is not cosmetic: each boundary
38
38
  | Package | Runs in | Responsibility | Hard rule |
39
39
  | --- | --- | --- | --- |
40
40
  | `@reticlehq/core` | both | The **wire contract**: every constant + zod schema crossing any boundary | Depends only on `zod` and `open-verification` |
41
+ | `open-verification` | both | The Open Verification Protocol: vocabulary, rules, `adjudicate()` | Depends only on `zod` |
42
+ | `@reticlehq/engine` | Node | The rules that decide a verdict | No browser, no daemon, no CLI |
43
+ | `@reticlehq/init` | Node (build time) | `reticle init`: writes files and runs the package manager | Never opens a socket |
41
44
  | `@reticlehq/browser` | the browser | Instrument the page; execute commands; emit events | Never imports Node APIs |
42
45
  | `@reticlehq/server` | Node | The bridge, the MCP server, the `reticle` CLI, flow/run storage | Never imports DOM APIs |
43
46
  | `@reticlehq/react` | the browser | The SDK **kit** you install in a browser app: re-exports the browser sensor (so one install gives both `reticle` and `install`) and maps a DOM node → React component → source `file:line` | Core works without the source-mapping half |
@@ -57,7 +60,7 @@ The browser and server both import it; neither can invent a message the other do
57
60
 
58
61
  ## The data flow, end to end
59
62
 
60
- 1. **Connect.** In dev, your app calls `reticle.connect({ session })`. The SDK opens a WebSocket to the bridge (`ws://localhost:4400/reticle` by default) and sends a `HELLO` carrying the session id, protocol version, and (if configured) a pairing token. Each browser tab uses `SESSION_AUTO` (a unique id), so multiple apps/tabs never collide.
63
+ 1. **Connect.** In dev, your app calls `reticle.connect({ session })`. The SDK opens a WebSocket to the bridge (`ws://localhost:4400/reticle` by default) and sends a `HELLO` carrying the session id, protocol version, and (if configured) a pairing token. Each browser tab uses `SESSION_AUTO` (a unique id), so multiple apps/tabs never collide. A second `connect()` on a live page does not open a second socket. If it names a `projectId` and the first call did not, the SDK sends `HELLO` again with the id, and the bridge moves the session to that project. An app that calls `connect()` itself, before the build plugin's `connect({ projectId })`, keeps its project this way.
61
64
  2. **Capture.** The SDK installs observers: a `MutationObserver` for DOM changes, wrappers around `fetch`/`XHR` for network, a console hook, a history hook for routing, and registries the app opts into (`registerStore`, `registerCapabilities`, `reticle.signal`). Events flow into a bounded **ring buffer**, so recent history is always available and memory is capped.
62
65
  3. **Look / act.** The agent calls an MCP tool. `reticle_look { action: "find" }` finds an element by role/text/testid/ component and returns a stable **ref**. `reticle_act` dispatches an action against that ref and returns an **effect** report (did it land, did the DOM mutate, did focus move…). The server sends the command over the WebSocket; the SDK runs it and replies.
63
66
  4. **Observe.** After an action, the agent reads what happened with `reticle_observe { action: "network" }`, `reticle_observe { action: "console" }`, `reticle_look { action: "state" }`, or the reaction digest from `reticle_act_and_wait`. The server pulls the relevant slice of the ring buffer (scoped to a cursor so stale events can't leak in) and returns a compact summary.
@@ -79,15 +82,15 @@ Reticle is honest about which tier a given assertion used, so "green" carries it
79
82
 
80
83
  ### 2. Structured reads, not screenshots
81
84
 
82
- A screenshot is ~1,365 image tokens per look, slow, non-deterministic, and **blind to everything non-visual**: the failed request, the console error, the route that didn't change. Reticle reads the accessibility tree, the network log, the console, and framework state as compact structured data. That's an order of magnitude cheaper _and_ it sees the bugs pixels can't. See [benchmarks.md](benchmarks.md) for the measured comparison.
85
+ A screenshot is ~1,365 image tokens per look, slow, non-deterministic, and **blind to everything non-visual**: the failed request, the console error, the route that didn't change. Reticle reads the accessibility tree, the network log, the console, and framework state as compact structured data. That's an order of magnitude cheaper _and_ it sees the bugs pixels can't. See [Benchmarks](/benchmarks) for the measured comparison.
83
86
 
84
87
  ### 3. Record once, replay deterministically
85
88
 
86
89
  The same verification runs over and over, every commit, every CI run. Reticle records a **flow** once, then replays it with **no AI model**: it re-resolves each element's durable anchor against the live DOM and re-asserts the declared consequence. A CI gate diffs the verdict exactly, at ~0% flake, for a couple hundred tokens, versus an agent re-driving the whole flow with the model every time. Self- healing rebinds a drifted anchor **only if the consequence still fires**, so it never "heals to the wrong element and ships the regression."
87
90
 
88
- ### 4. Dev-only, localhost-only, your app data stays local
91
+ ### 4. Development by default, your app data stays local
89
92
 
90
- The SDK is tree-shaken out of production builds and connects only to a local bridge. The bridge binds to loopback by default; exposing it beyond localhost _requires_ a pairing token (and the server refuses to start on a non-loopback host unless `RETICLE_ALLOWED_ORIGINS` names the app's origins). Every environment variable that gates a security control is a single named constant, so a typo can't silently disable auth. Nothing from the app under test leaves your machine unless you link a project: no DOM, network, console, state, or source. What a linked project syncs is set by `reticle config`; see [what is recorded](what-is-recorded.md). The CLI reports anonymous, opt-out usage metrics only (a random id + event names like `cli_command_run`/`daemon_started`; no code, no PII; see [telemetry](telemetry.md)); opt out with `reticle telemetry disable`, `RETICLE_TELEMETRY=0`, or `DO_NOT_TRACK=1`. The one thing that carries free text is feedback you or your agent deliberately send (`reticle feedback` / `reticle_session { action: "feedback" }`). It is never collected passively, it is redacted before sending, and it is disabled separately with `RETICLE_FEEDBACK=0`.
93
+ By default the SDK is tree-shaken out of production builds and connects only to a local bridge. [Production mode](/production-mode) is the one exception: it is opt-in, and a page loads the SDK only with a signed, single-use activation ticket. The bridge binds to loopback by default. It refuses a connect without the pairing token, also on localhost. Exposing it beyond localhost also needs the token (and the server refuses to start on a non-loopback host unless `RETICLE_ALLOWED_ORIGINS` names the app's origins). Every environment variable that gates a security control is a single named constant, so a typo can't silently disable auth. Nothing from the app under test leaves your machine unless you link a project: no DOM, network, console, state, or source. What a linked project syncs is set by `reticle config`; see [what is recorded](/what-is-recorded). The CLI reports anonymous, opt-out usage metrics only (a random id + event names like `cli_command_run`/`daemon_started`; no code, no PII; see [telemetry](/telemetry)); opt out with `reticle telemetry disable`, `RETICLE_TELEMETRY=0`, or `DO_NOT_TRACK=1`. The one thing that carries free text is feedback you or your agent deliberately send (`reticle feedback` / `reticle_session { action: "feedback" }`). It is never collected passively, it is redacted before sending, and it is disabled separately with `RETICLE_FEEDBACK=0`.
91
94
 
92
95
  ---
93
96
 
@@ -107,7 +110,7 @@ This is plain, reviewable, version-controllable data, not a black box.
107
110
  ## Running at scale (multiple apps, multiple projects)
108
111
 
109
112
  - **Multiple apps / tabs on one bridge:** fine. Each connection has a unique `SESSION_AUTO` id; a tool call targets the focused/most-recent session, or you pass an explicit `sessionId`.
110
- - **Multiple isolated projects:** give each project its own bridge port via `RETICLE_PORT` (set it in the MCP config and dial the same port from the app). A port already in use fails fast with a clear error rather than hanging. See [getting-started.md → Running multiple apps](getting-started.md#running-multiple-apps-at-once).
113
+ - **Multiple isolated projects:** give each project its own bridge port via `RETICLE_PORT` (set it in the MCP config and dial the same port from the app). A port already in use fails fast with a clear error rather than hanging. See [Getting started → Running multiple apps at once](/getting-started#running-multiple-apps-at-once).
111
114
  - **CI / no MCP:** `reticle verify <url>` replays the saved flows headlessly and exits non-zero on failure. It writes the same verdict artifact the MCP path produces, with no agent in the loop.
112
115
 
113
116
  ---
@@ -125,7 +128,7 @@ See [LICENSE](https://github.com/reticlehq/reticle/blob/main/LICENSE) and each p
125
128
 
126
129
  ## Where to go next
127
130
 
128
- - [getting-started.md](getting-started.md) wires Reticle into your app in a couple of minutes.
129
- - [benchmarks.md](benchmarks.md) covers how we measure, and the honest results vs the alternatives.
130
- - [usage.md](usage.md) is the full tool reference and advanced modes.
131
+ - [Getting started](/getting-started) wires Reticle into your app in a couple of minutes.
132
+ - [Benchmarks](/benchmarks) covers how we measure, and the honest results against the alternatives.
133
+ - [Usage](/usage) is the full tool reference and advanced modes.
131
134
  - [CONTRIBUTING.md](https://github.com/reticlehq/reticle/blob/main/CONTRIBUTING.md) has the development loop and the rules.
package/docs/autodrive.md CHANGED
@@ -56,7 +56,7 @@ Two arguments of the flow replay make the branches possible, and you can use the
56
56
 
57
57
  ## What it needs
58
58
 
59
- **A project on the Reticle platform.** Every plan includes the Harness. A new account gets 10 free credits (about two full drives of a small app), a card starts a 14-day trial with 500, and the paid plan includes 1,000 credits per seat each month. Run `reticle connect` once in your app's folder. The Harness runs on the platform: planning, personas, and every choice of what to press next. A model key of your own does not drive it, and a machine with no platform link gets the refusal and the way to fix it.
59
+ **A project on the Reticle platform.** Every plan includes the Harness. A new account gets free credits to try it once, a card starts a 14-day trial with 500, and the paid plan includes 1,000 credits per seat each month. Run `reticle connect` once in your app's folder, then switch the Harness on for the project (`npx @reticlehq/server config --harness on`, or the switch in the HUD). The Harness runs on the platform: planning, personas, and every choice of what to press next. A model key of your own does not drive it, and a machine with no platform link gets the refusal and the way to fix it.
60
60
 
61
61
  | Variable | What it does |
62
62
  | --- | --- |
@@ -68,20 +68,21 @@ Two arguments of the flow replay make the branches possible, and you can use the
68
68
 
69
69
  ## How it decides
70
70
 
71
- The platform drives with Jev, a System One model that answers typed questions and cannot write. Every choice is a selection from candidates read off the page: which element, which tool, what consequence to claim. Three rules sit around those choices, because each one was a drive that stopped short or claimed nothing:
71
+ The platform drives with Jev, a System One model that answers typed questions and cannot write. Every choice is a selection from candidates read off the page: which element, which tool, what consequence to claim. Four rules sit around those choices:
72
72
 
73
73
  - A journey that names a page it has not reached ("then turn on automatic refunds in Settings") is not finished. The Harness goes there instead of stopping.
74
74
  - A dialog whose destructive action already ran is closed rather than treated as the end of the journey.
75
75
  - A control the destructive-action gate refused is a write, so the Harness always claims a request or a signal for it. A save the server rejects then fails visibly instead of passing as nothing to prove.
76
+ - The Harness takes an outward action (invite, send, share, delete, publish, revoke, sign out, change a role, create a key, change an item's state) only when the person's journey asks for it in so many words. Paying is not read from a button's words: "Start free trial", "Upgrade", "Subscribe" and "Place order" are pressed like any other control. Money moves when somebody types card or bank details (a field whose autocomplete is `cc-number`, `cc-csc` or `cc-exp`, or whose name or label says card number, CVC, expiry or IBAN) or leaves for a payment provider (Stripe, Dodo Payments checkout, Paddle, Lemon Squeezy, PayPal, Razorpay, Braintree). The Harness does those two only when the journey asks to pay.
76
77
 
77
78
  ## Turning it off, and who pays for it
78
79
 
79
80
  A project linked to a Reticle workspace reads two things from it before a drive starts, and honours both:
80
81
 
81
- - **The switch.** Autonomous driving can be turned off per project, from the HUD's Reticle Harness switch or the dashboard (Settings → Projects → Reticle Harness on <project>). A drive then refuses and says where to turn it back on. Switched off while a platform drive is running, the drive stops at its next turn with `stopReason: "stopped"`, keeps what it drove, and says so in the Agent Log. Everything else is unaffected, including the tools your own agent drives with.
82
- - **Credits.** Harness drives and dashboard chat checks spend Harness credits. The platform sets the amount for your plan (see above). The HUD shows how many are left; once they are spent a drive is refused with that number and the way to get more.
82
+ - **The switch.** The Harness is off for a new project, because each drive spends credits. Switch it on for each project: use the Reticle Harness switch in the HUD, the dashboard (Settings → Projects), or run `npx @reticlehq/server config --harness on` in the project. While it is off, a drive refuses and tells you how to switch it on. If you switch it off while a platform drive runs, the drive stops at its next turn with `stopReason: "stopped"`, keeps what it drove, and says so in the Agent Log. The switch does not change the tools that your own agent drives with.
83
+ - **Credits.** Harness drives and dashboard chat checks spend credits, the same credits that pay for the rest of the platform. The platform sets the amount for your plan (see above). The HUD shows how many are left; once they are spent a drive is refused with that number and the way to get more.
83
84
 
84
- If only the settings endpoint is unreachable, the on/off switch and the credit check do not block a drive: an unreachable settings endpoint is not a reason to lose a feature you were never told to stop using. The drive itself still needs the platform, so with no connection at all it is refused with the reason.
85
+ If Reticle cannot read the project settings from the platform, the drive does not start. It needs a confirmed yes from the platform for the switch and the credits. Try again when the platform answers.
85
86
 
86
87
  ## What it will not do
87
88
 
@@ -160,7 +160,7 @@ Measured on the observation-cost pass (numbers regenerate from `bench/raw/`). Th
160
160
  | Playwright MCP | 9 / 10 | 0.91 | 1,292 | 6.97 |
161
161
 
162
162
  - **Reticle is the only tool that caught every regression**, with zero false alarms on the control.
163
- - DevTools is a hair cheaper per look (758 vs 815 tokens), **but it's cheaper because it catches less.** On the metric that combines both (Verification Efficiency), Reticle leads at 12.27 vs 10.55.
163
+ - DevTools is a little cheaper per look (758 against 815 tokens), **because it catches less.** Only Reticle passes the 100% catch gate, so only Reticle has a qualifying Verification Efficiency. The table shows the ungated value for the other two, for comparison only: 12.27 against 10.55 and 6.97.
164
164
  - Playwright catches a lot but is ~1.6× more expensive per look, so its efficiency is lowest here.
165
165
 
166
166
  ### 5c. A real production app (the Reticle dashboard)
@@ -284,7 +284,7 @@ Reticle is **most efficient exactly where big companies hurt most**: a large num
284
284
  - `bench/METRIC.md` holds the exact metric definitions.
285
285
  - `bench/METHODOLOGY.md`: full design (controls, scenarios, fairness).
286
286
  - `bench/history.jsonl`: every measured run over time.
287
- - `bench/raw/` holds raw payloads and per-scenario results (gitignored locally; regenerated by `pnpm bench`).
287
+ - `bench/raw/` holds raw payloads and per-scenario results. `pnpm bench` regenerates them. Git ignores them, except the five files that a published number rests on (`schema-tax.json`, `compiled-suite-vs-replay.json`, `suite-rre.json`, `leak-stress.json`, `stress-tiers.json`).
288
288
 
289
289
  ---
290
290
 
@@ -4,7 +4,7 @@ description: Coverage, storage, fake clocks, autonomous crawling, visual diffing
4
4
  icon: sparkles
5
5
  ---
6
6
 
7
- These are the Reticle tools that do something other than look, act, observe and assert: control coverage, client storage, a fake clock, autonomous crawling, semantic and visual baselines, network mocking, flow recording and replay, and the CI artifact. Reach for them when the verify loop is not the shape of the question. They are **not advertised on the default tool surface, but they are reachable from it**: `reticle_run({ tool, args })` dispatches to any registered tool by name, advertised or not, which is the spelling used on this page. Starting the daemon with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read once at startup, so restart it) advertises some of them outright, with their output schemas: storage, clock, screenshot, visual diff, network mocking, recording, flow save and replay. The rest stay behind `reticle_run` on every surface.
7
+ These are the Reticle tools that do something other than look, act, observe and assert: control coverage, client storage, a fake clock, autonomous crawling, semantic and visual baselines, network mocking, flow recording and replay, and the CI artifact. Reach for them when the verify loop is not the shape of the question. They are **not advertised on the default tool surface, but they are reachable from it**: `reticle_run({ tool, args })` dispatches to any registered tool by name, advertised or not, which is the spelling used on this page. Starting the daemon with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read once at startup, so restart it) advertises more tools outright, with their output schemas: capabilities, intent, context, storage, clock, screenshot, visual diff, network mocking, recording, flow save and replay. That surface uses the separate tool names (`reticle_sessions`, `reticle_snapshot`, `reticle_query`, `reticle_network`, `reticle_console`, `reticle_wait_for`), not `reticle_look` and the `action` forms that these docs use. The rest stay behind `reticle_run` on every surface.
8
8
 
9
9
  Most of the time you want the verify loop. Look, act, observe, assert. These are the tools for the times you want something else, and they are the ones people are most surprised exist.
10
10