@reticlehq/server 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. package/SKILL.md +30 -28
  2. package/dist/command/cli/adhoc-suite.d.ts +19 -0
  3. package/dist/command/cli/adhoc-suite.js +88 -0
  4. package/dist/command/cli/adhoc-verdict.d.ts +23 -2
  5. package/dist/command/cli/adhoc-verdict.js +18 -10
  6. package/dist/command/cli/auth/device-flow.d.ts +2 -2
  7. package/dist/command/cli/cli-doctor.js +37 -5
  8. package/dist/command/cli/cli-flow-commands.d.ts +2 -1
  9. package/dist/command/cli/cli-kill.js +1 -1
  10. package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
  11. package/dist/command/cli/cli-parse-grammar.js +7 -0
  12. package/dist/command/cli/cli-parse-verify.d.ts +2 -5
  13. package/dist/command/cli/cli-parse-verify.js +86 -13
  14. package/dist/command/cli/cli-parse.d.ts +8 -1
  15. package/dist/command/cli/cli-parse.js +38 -7
  16. package/dist/command/cli/cli-verify.d.ts +59 -0
  17. package/dist/command/cli/cli-verify.js +131 -19
  18. package/dist/command/cli/cloud-cli.js +7 -6
  19. package/dist/command/cli/cloud-kit.d.ts +0 -2
  20. package/dist/command/cli/cloud-kit.js +3 -7
  21. package/dist/command/cli/config/config-discovery.d.ts +12 -0
  22. package/dist/command/cli/config/config-discovery.js +19 -0
  23. package/dist/command/cli/doctor/doctor-rows.d.ts +12 -0
  24. package/dist/command/cli/doctor/doctor-rows.js +12 -0
  25. package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
  26. package/dist/command/cli/drive/drive-command.js +1 -1
  27. package/dist/command/cli/launch/cli-launch.d.ts +3 -23
  28. package/dist/command/cli/launch/cli-launch.js +29 -70
  29. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +33 -0
  30. package/dist/command/cli/lifecycle/daemon-lifecycle.js +198 -0
  31. package/dist/command/cli/mcp-command.js +5 -3
  32. package/dist/command/cli/ports/resolve/cli-port.d.ts +15 -1
  33. package/dist/command/cli/ports/resolve/cli-port.js +50 -2
  34. package/dist/command/cli/report-command.d.ts +16 -0
  35. package/dist/command/cli/report-command.js +97 -0
  36. package/dist/command/cli/status/status-lines.d.ts +28 -0
  37. package/dist/command/cli/status/status-lines.js +117 -0
  38. package/dist/command/cli.d.ts +1 -1
  39. package/dist/command/cli.js +29 -177
  40. package/dist/command/daemon/binding/daemon-status-probe.d.ts +32 -0
  41. package/dist/command/daemon/binding/daemon-status-probe.js +93 -0
  42. package/dist/command/daemon/binding/loopback-reach.d.ts +42 -0
  43. package/dist/command/daemon/binding/loopback-reach.js +63 -0
  44. package/dist/command/daemon/binding/port-presence.d.ts +1 -1
  45. package/dist/command/daemon/daemon-resilience.d.ts +13 -1
  46. package/dist/command/daemon/daemon-resilience.js +12 -1
  47. package/dist/command/daemon/daemon.d.ts +2 -11
  48. package/dist/command/daemon/daemon.js +8 -11
  49. package/dist/command/daemon/idle-shutdown.d.ts +2 -1
  50. package/dist/command/daemon/lifetime/heartbeat.d.ts +8 -0
  51. package/dist/command/daemon/lifetime/heartbeat.js +10 -0
  52. package/dist/command/daemon/lifetime/log-cap.d.ts +68 -0
  53. package/dist/command/daemon/lifetime/log-cap.js +115 -0
  54. package/dist/command/setup/agent-notice-memory.d.ts +16 -0
  55. package/dist/command/setup/agent-notice-memory.js +65 -0
  56. package/dist/command/setup/bringup/dev-server-wait.d.ts +1 -0
  57. package/dist/command/setup/bringup/dev-server-wait.js +16 -0
  58. package/dist/command/setup/bringup/ensure-daemon.js +1 -1
  59. package/dist/command/setup/init/init-host.js +8 -0
  60. package/dist/command/setup/init/init-runtime.js +13 -1
  61. package/dist/command/setup/node-effects.js +1 -1
  62. package/dist/command/setup/probe/page-probe.js +9 -2
  63. package/dist/command/setup/run-setup.d.ts +25 -7
  64. package/dist/command/setup/run-setup.js +51 -9
  65. package/dist/command/setup/setup-command.js +31 -3
  66. package/dist/command/setup/terminal/confirm.js +2 -1
  67. package/dist/command/update/updater.d.ts +27 -0
  68. package/dist/command/update/updater.js +35 -2
  69. package/dist/features/crawl/crawl-tools.js +3 -1
  70. package/dist/features/crawl/crawl.d.ts +2 -0
  71. package/dist/features/crawl/crawl.js +3 -1
  72. package/dist/features/harness/drive-report.d.ts +73 -0
  73. package/dist/features/harness/drive-report.js +196 -0
  74. package/dist/features/harness/driver.js +29 -8
  75. package/dist/features/harness/drivers.d.ts +19 -0
  76. package/dist/features/harness/drivers.js +19 -0
  77. package/dist/features/harness/fill-values.d.ts +67 -0
  78. package/dist/features/harness/fill-values.js +153 -0
  79. package/dist/features/harness/jev-driver.d.ts +188 -0
  80. package/dist/features/harness/jev-driver.js +661 -0
  81. package/dist/features/harness/openai-driver.d.ts +46 -0
  82. package/dist/features/harness/openai-driver.js +197 -0
  83. package/dist/features/harness/platform-config.d.ts +62 -0
  84. package/dist/features/harness/platform-config.js +97 -0
  85. package/dist/features/visual/visual-tools.js +26 -2
  86. package/dist/index.d.ts +1 -1
  87. package/dist/index.js +80 -130
  88. package/dist/judgement/capsule/capsule-store.d.ts +31 -2
  89. package/dist/judgement/capsule/capsule-store.js +58 -6
  90. package/dist/judgement/capsule/capsule.js +5 -0
  91. package/dist/judgement/capsule/causal-summary.d.ts +13 -1
  92. package/dist/judgement/capsule/causal-summary.js +7 -0
  93. package/dist/judgement/domain/domain-model.js +38 -9
  94. package/dist/judgement/domain/domain-tools.js +4 -2
  95. package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
  96. package/dist/judgement/phenomena/phenomena.d.ts +2 -1
  97. package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
  98. package/dist/judgement/runs/artifact/gap-report.js +46 -0
  99. package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
  100. package/dist/judgement/runs/artifact/gap-summary.js +85 -0
  101. package/dist/judgement/runs/artifact/render-report.js +4 -1
  102. package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
  103. package/dist/judgement/runs/artifact/to-artifact.js +1 -1
  104. package/dist/judgement/runs/context-tools.js +21 -3
  105. package/dist/judgement/runs/drive-run.js +7 -2
  106. package/dist/judgement/runs/verification-sync.d.ts +2 -2
  107. package/dist/judgement/runs/verification-sync.js +9 -2
  108. package/dist/language/flows/affected-tools.js +6 -3
  109. package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
  110. package/dist/language/flows/annotate-notes/annotate.js +66 -18
  111. package/dist/language/flows/flow-anchor.d.ts +12 -0
  112. package/dist/language/flows/flow-anchor.js +20 -0
  113. package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
  114. package/dist/language/flows/flow-expect-grammar.js +92 -11
  115. package/dist/language/flows/flow-intent.js +1 -1
  116. package/dist/language/flows/flow-learning.d.ts +3 -0
  117. package/dist/language/flows/flow-learning.js +39 -8
  118. package/dist/language/flows/flow-replay-run.d.ts +55 -10
  119. package/dist/language/flows/flow-replay-run.js +217 -58
  120. package/dist/language/flows/flow-replay-types.d.ts +9 -0
  121. package/dist/language/flows/flow-replay.d.ts +29 -11
  122. package/dist/language/flows/flow-replay.js +221 -203
  123. package/dist/language/flows/flow-result.d.ts +18 -1
  124. package/dist/language/flows/flow-result.js +18 -2
  125. package/dist/language/flows/flow-step-runners.d.ts +26 -10
  126. package/dist/language/flows/flow-step-runners.js +20 -4
  127. package/dist/language/flows/flow-store-for-session.d.ts +2 -1
  128. package/dist/language/flows/flow-success.d.ts +21 -6
  129. package/dist/language/flows/flow-success.js +50 -138
  130. package/dist/language/flows/flow-tools.js +8 -4
  131. package/dist/language/flows/flows.d.ts +15 -10
  132. package/dist/language/flows/flows.js +17 -4
  133. package/dist/language/flows/heal.d.ts +9 -1
  134. package/dist/language/flows/heal.js +24 -2
  135. package/dist/language/flows/learned-sources.d.ts +21 -0
  136. package/dist/language/flows/learned-sources.js +84 -0
  137. package/dist/language/flows/narrow-write.d.ts +4 -4
  138. package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
  139. package/dist/language/flows/recording/recording-backtrack.js +1 -1
  140. package/dist/language/flows/recording/replay-halt.d.ts +2 -1
  141. package/dist/language/flows/recording/tape/recordings.d.ts +2 -2
  142. package/dist/language/flows/replay.js +13 -9
  143. package/dist/language/flows/stores/annotation-store.d.ts +5 -5
  144. package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
  145. package/dist/language/flows/stores/flake-store.js +2 -3
  146. package/dist/language/flows/suite/coverage.d.ts +1 -1
  147. package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
  148. package/dist/language/flows/suite-selection.d.ts +15 -1
  149. package/dist/language/flows/suite-selection.js +1 -1
  150. package/dist/language/flows/verify-change-tools.js +21 -4
  151. package/dist/memory/cloud/account-state.d.ts +1 -1
  152. package/dist/memory/cloud/account-state.js +5 -4
  153. package/dist/memory/cloud/cloud-config.d.ts +9 -0
  154. package/dist/memory/cloud/cloud-config.js +9 -0
  155. package/dist/memory/cloud/cloud-sync.d.ts +7 -4
  156. package/dist/memory/cloud/cloud-sync.js +11 -6
  157. package/dist/memory/cloud/harness-config.d.ts +12 -0
  158. package/dist/memory/cloud/harness-config.js +58 -0
  159. package/dist/memory/cloud/harness-offer.d.ts +44 -0
  160. package/dist/memory/cloud/harness-offer.js +146 -0
  161. package/dist/memory/cloud/harness-switch.d.ts +11 -0
  162. package/dist/memory/cloud/harness-switch.js +53 -0
  163. package/dist/memory/cloud/sync-cycle.d.ts +12 -0
  164. package/dist/memory/cloud/sync-cycle.js +29 -4
  165. package/dist/memory/cloud/sync-daemon.d.ts +35 -2
  166. package/dist/memory/cloud/sync-daemon.js +45 -0
  167. package/dist/memory/cloud/sync-disk.js +11 -6
  168. package/dist/memory/impact/impact-recorder.d.ts +8 -0
  169. package/dist/memory/impact/impact-recorder.js +2 -1
  170. package/dist/memory/impact/impact-store.d.ts +5 -0
  171. package/dist/memory/impact/impact-store.js +25 -0
  172. package/dist/memory/intent/inline-intent.js +8 -3
  173. package/dist/memory/intent/intent-shard-store.d.ts +33 -47
  174. package/dist/memory/intent/intent-shard-store.js +253 -141
  175. package/dist/memory/intent/intent-shard.d.ts +22 -23
  176. package/dist/memory/intent/intent-shard.js +1 -1
  177. package/dist/memory/intent/intent-store.d.ts +13 -8
  178. package/dist/memory/intent/intent-store.js +46 -90
  179. package/dist/memory/intent/intent-tools.js +34 -23
  180. package/dist/memory/journal/ambient-store.js +2 -3
  181. package/dist/memory/journal/attach-journal.d.ts +11 -0
  182. package/dist/memory/journal/attach-journal.js +1 -1
  183. package/dist/memory/journal/deviation-report.js +4 -1
  184. package/dist/memory/journal/deviation-service.js +10 -2
  185. package/dist/memory/journal/drive-flow.d.ts +2 -2
  186. package/dist/memory/journal/drive-flow.js +21 -12
  187. package/dist/memory/journal/envelope-store.js +2 -3
  188. package/dist/memory/journal/journal-query.d.ts +35 -1
  189. package/dist/memory/journal/journal-query.js +46 -0
  190. package/dist/memory/journal/journal-recorder.d.ts +71 -2
  191. package/dist/memory/journal/journal-recorder.js +45 -7
  192. package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
  193. package/dist/memory/journal/on-disk/retain-policy.js +44 -0
  194. package/dist/memory/journal/on-disk/retention.d.ts +91 -2
  195. package/dist/memory/journal/on-disk/retention.js +181 -11
  196. package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
  197. package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
  198. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -2
  199. package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -38
  200. package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
  201. package/dist/memory/journal/on-disk/workspace-tiers.js +88 -0
  202. package/dist/memory/journal/route-template.d.ts +22 -0
  203. package/dist/memory/journal/route-template.js +44 -0
  204. package/dist/memory/journal/self-observation.d.ts +3 -0
  205. package/dist/memory/journal/self-observation.js +67 -0
  206. package/dist/memory/journal/session-end.d.ts +17 -4
  207. package/dist/memory/journal/session-end.js +85 -7
  208. package/dist/memory/journal/session-journal.d.ts +122 -5
  209. package/dist/memory/journal/session-journal.js +489 -32
  210. package/dist/memory/project/artifact-root-resolver.d.ts +3 -0
  211. package/dist/memory/project/artifact-root-resolver.js +89 -0
  212. package/dist/memory/project/artifact-root.d.ts +39 -2
  213. package/dist/memory/project/artifact-root.js +58 -1
  214. package/dist/memory/project/baselines.d.ts +1 -1
  215. package/dist/memory/project/dir/fill-value-store.d.ts +32 -0
  216. package/dist/memory/project/dir/fill-value-store.js +88 -0
  217. package/dist/memory/project/dir/reticle-dir.d.ts +15 -5
  218. package/dist/memory/project/dir/reticle-dir.js +11 -0
  219. package/dist/memory/project/fs/fs-port.d.ts +22 -1
  220. package/dist/memory/project/fs/fs-port.js +72 -5
  221. package/dist/memory/project/fs/write-atomic.d.ts +26 -0
  222. package/dist/memory/project/fs/write-atomic.js +48 -0
  223. package/dist/memory/project/learned-routes.d.ts +16 -2
  224. package/dist/memory/project/learned-routes.js +26 -10
  225. package/dist/memory/project/project-for-root.d.ts +30 -0
  226. package/dist/memory/project/project-for-root.js +43 -0
  227. package/dist/memory/project/project-tools.js +10 -5
  228. package/dist/memory/project/session-root.d.ts +35 -6
  229. package/dist/memory/project/session-root.js +70 -8
  230. package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
  231. package/dist/memory/recall/prior/connection-memory.js +8 -0
  232. package/dist/memory/recall/registered-projects.d.ts +2 -1
  233. package/dist/portal/bridge/auth-failure-reason.d.ts +9 -0
  234. package/dist/portal/bridge/auth-failure-reason.js +20 -3
  235. package/dist/portal/bridge/bridge.d.ts +6 -1
  236. package/dist/portal/bridge/bridge.js +36 -2
  237. package/dist/portal/input/real-input.js +2 -1
  238. package/dist/portal/pool/browser-pool.d.ts +10 -0
  239. package/dist/portal/pool/browser-pool.js +10 -0
  240. package/dist/portal/pool/playwright-launcher.js +1 -0
  241. package/dist/portal/pool/pool-contract.d.ts +2 -0
  242. package/dist/portal/pool/storage-seed.d.ts +2 -1
  243. package/dist/portal/pool/zero-install.d.ts +27 -0
  244. package/dist/portal/pool/zero-install.js +54 -0
  245. package/dist/portal/realm/web-realm.js +6 -0
  246. package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
  247. package/dist/portal/session/dev-server/served-document.d.ts +21 -0
  248. package/dist/portal/session/dev-server/served-document.js +57 -0
  249. package/dist/portal/session/facts/background-traffic.d.ts +9 -0
  250. package/dist/portal/session/facts/background-traffic.js +25 -0
  251. package/dist/portal/session/facts/session-replaced.d.ts +19 -0
  252. package/dist/portal/session/facts/session-replaced.js +22 -0
  253. package/dist/portal/session/live-control-tools.js +48 -3
  254. package/dist/portal/session/no-session-diagnosis.d.ts +1 -0
  255. package/dist/portal/session/no-session-diagnosis.js +67 -60
  256. package/dist/portal/session/no-session-next-action.js +3 -2
  257. package/dist/portal/session/no-session-watch.js +14 -10
  258. package/dist/portal/session/presence/attachment-history.d.ts +13 -2
  259. package/dist/portal/session/rebind.d.ts +18 -0
  260. package/dist/portal/session/rebind.js +38 -0
  261. package/dist/portal/session/session-health.d.ts +61 -0
  262. package/dist/portal/session/session-health.js +40 -1
  263. package/dist/portal/session/session-info.d.ts +6 -0
  264. package/dist/portal/session/session-manager.d.ts +13 -0
  265. package/dist/portal/session/session-manager.js +42 -3
  266. package/dist/portal/session/session-successor.d.ts +11 -0
  267. package/dist/portal/session/session-successor.js +14 -1
  268. package/dist/portal/session/session-verdict-facts.d.ts +28 -0
  269. package/dist/portal/session/session-verdict-facts.js +15 -0
  270. package/dist/portal/session/session.d.ts +26 -10
  271. package/dist/portal/session/session.js +48 -49
  272. package/dist/status-payload.d.ts +12 -1
  273. package/dist/status-payload.js +4 -1
  274. package/dist/surface/http-server.js +3 -5
  275. package/dist/surface/mcp/faults/mcp-outage.d.ts +2 -1
  276. package/dist/surface/mcp/faults/mcp-outage.js +20 -4
  277. package/dist/surface/mcp/mcp-proxy.d.ts +10 -1
  278. package/dist/surface/mcp/mcp-proxy.js +23 -12
  279. package/dist/surface/mcp/mcp.d.ts +10 -1
  280. package/dist/surface/mcp/mcp.js +59 -29
  281. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +15 -2
  282. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +22 -5
  283. package/dist/surface/mcp/proxy/proxy-log.d.ts +5 -11
  284. package/dist/surface/mcp/proxy/proxy-log.js +7 -22
  285. package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
  286. package/dist/surface/mcp/server-instructions.js +12 -1
  287. package/dist/surface/tools/act/act-preflight.js +6 -1
  288. package/dist/surface/tools/act/already-true.d.ts +40 -0
  289. package/dist/surface/tools/act/already-true.js +36 -0
  290. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +6 -0
  291. package/dist/surface/tools/act/navigation/navigate-arrival.js +13 -0
  292. package/dist/surface/tools/act/sequence-grade.js +13 -0
  293. package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
  294. package/dist/surface/tools/act/settle-in-flight.js +29 -6
  295. package/dist/surface/tools/act-capsule.js +49 -8
  296. package/dist/surface/tools/act-tools.js +25 -24
  297. package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
  298. package/dist/surface/tools/args/numeric-bounds.js +30 -3
  299. package/dist/surface/tools/assert/assert-grade.js +14 -2
  300. package/dist/surface/tools/assert/assert-verdict.js +74 -21
  301. package/dist/surface/tools/browser-tools.js +4 -3
  302. package/dist/surface/tools/coverage-tools.js +5 -2
  303. package/dist/surface/tools/dynamic-tools.js +43 -46
  304. package/dist/surface/tools/error-recovery.d.ts +6 -0
  305. package/dist/surface/tools/error-recovery.js +15 -0
  306. package/dist/surface/tools/explore-tools.js +80 -7
  307. package/dist/surface/tools/feedback-tools.d.ts +2 -1
  308. package/dist/surface/tools/feedback-tools.js +50 -3
  309. package/dist/surface/tools/first-sentence.d.ts +24 -0
  310. package/dist/surface/tools/first-sentence.js +0 -0
  311. package/dist/surface/tools/harness-explore.d.ts +125 -1
  312. package/dist/surface/tools/harness-explore.js +401 -15
  313. package/dist/surface/tools/harness-plan.d.ts +76 -0
  314. package/dist/surface/tools/harness-plan.js +156 -0
  315. package/dist/surface/tools/harness-toolset.js +12 -1
  316. package/dist/surface/tools/invoke-tool.js +82 -9
  317. package/dist/surface/tools/lease-hint.d.ts +8 -0
  318. package/dist/surface/tools/lease-hint.js +10 -0
  319. package/dist/surface/tools/lease-tools.d.ts +16 -28
  320. package/dist/surface/tools/lease-tools.js +53 -5
  321. package/dist/surface/tools/lineage-tools.js +1 -1
  322. package/dist/surface/tools/live-call-text.d.ts +6 -0
  323. package/dist/surface/tools/live-call-text.js +36 -2
  324. package/dist/surface/tools/observe-tools.js +69 -15
  325. package/dist/surface/tools/read/query-windowed.d.ts +1 -1
  326. package/dist/surface/tools/read/query-windowed.js +1 -1
  327. package/dist/surface/tools/read-tools.js +1 -2
  328. package/dist/surface/tools/real-input-attempt.d.ts +0 -5
  329. package/dist/surface/tools/real-input-attempt.js +1 -1
  330. package/dist/surface/tools/tool-kit.d.ts +23 -3
  331. package/dist/surface/tools/tool-kit.js +6 -1
  332. package/dist/surface/tools/tool-surface.js +17 -5
  333. package/dist/surface/tools/tools-helpers.d.ts +8 -7
  334. package/dist/surface/tools/tools-helpers.js +11 -13
  335. package/dist/surface/tools/tools.js +70 -30
  336. package/dist/telemetry/bug-found.js +32 -2
  337. package/dist/telemetry/feedback-context.js +3 -3
  338. package/dist/telemetry/feedback-local.d.ts +1 -0
  339. package/dist/telemetry/feedback-local.js +1 -3
  340. package/dist/telemetry/feedback.d.ts +1 -1
  341. package/dist/telemetry/harness-drive.d.ts +6 -0
  342. package/dist/telemetry/harness-drive.js +39 -0
  343. package/dist/telemetry/hud-metrics.d.ts +32 -0
  344. package/dist/telemetry/hud-metrics.js +95 -0
  345. package/dist/telemetry/mcp-connection.d.ts +14 -1
  346. package/dist/telemetry/mcp-connection.js +17 -2
  347. package/dist/telemetry/onboarding-firsts.d.ts +30 -1
  348. package/dist/telemetry/onboarding-firsts.js +54 -12
  349. package/dist/telemetry/session-metrics.d.ts +5 -0
  350. package/dist/telemetry/session-metrics.js +15 -1
  351. package/dist/telemetry/verification-of.js +31 -3
  352. package/dist/wire-journal.d.ts +31 -0
  353. package/dist/wire-journal.js +100 -0
  354. package/docs/README.md +1 -0
  355. package/docs/agent-cheatsheet.md +7 -3
  356. package/docs/autodrive.md +42 -2
  357. package/docs/benchmarks.md +1 -1
  358. package/docs/capabilities.mdx +1 -1
  359. package/docs/cli/cloud.mdx +11 -9
  360. package/docs/cli/doctor.mdx +1 -0
  361. package/docs/cli/init.mdx +1 -0
  362. package/docs/cli/report.mdx +44 -0
  363. package/docs/cli/status.mdx +35 -20
  364. package/docs/cli.mdx +8 -18
  365. package/docs/desktop.mdx +1 -1
  366. package/docs/faq.mdx +1 -1
  367. package/docs/flows.md +11 -0
  368. package/docs/frameworks.mdx +113 -1
  369. package/docs/install-agentic.mdx +36 -0
  370. package/docs/install-manual.mdx +1 -1
  371. package/docs/packages/tauri.mdx +1 -1
  372. package/docs/packages.mdx +1 -1
  373. package/docs/predicates.mdx +144 -4
  374. package/docs/system-map.md +6 -4
  375. package/docs/telemetry.md +2 -1
  376. package/docs/token-efficiency.md +2 -2
  377. package/docs/tools/overview.mdx +1 -1
  378. package/docs/tools/query.mdx +10 -10
  379. package/docs/troubleshooting.mdx +8 -2
  380. package/docs/usage.md +2 -2
  381. package/docs/what-is-recorded.md +62 -0
  382. package/package.json +5 -5
  383. package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
  384. package/dist/judgement/outcome/predicate-to-expect.js +0 -148
@@ -16,6 +16,7 @@
16
16
  * than a rule saying it shouldn't.
17
17
  */
18
18
  import { BrowserLaunchKind, type ConnectFailure, type SessionSummary } from '@reticlehq/core/telemetry';
19
+ import type { HudUseData } from '@reticlehq/core';
19
20
  /**
20
21
  * Mutable counters for one daemon lifetime. A plain class, not a store: it is process-local, it never
21
22
  * persists, and it dies with the daemon.
@@ -24,6 +25,8 @@ export declare class SessionMetrics {
24
25
  #private;
25
26
  /** Clock injected — this file must stay testable without a real one, per the repo's clock rule. */
26
27
  constructor(now: () => number);
28
+ /** A person used the HUD. Names only; the wire schema has already narrowed it. */
29
+ recordHudUse(use: HudUseData): void;
27
30
  /**
28
31
  * Record a call starting. Returns a function to call when it finishes — that closure is what makes
29
32
  * timing correct under CONCURRENCY: several agents can be inside `runTool` at once, so a single
@@ -162,5 +165,7 @@ export declare const recordSdkFailure: (failure: {
162
165
  site: string;
163
166
  message: string;
164
167
  }) => void;
168
+ /** Narrow-and-record one HUD use. Keeps the session hot path to a single call. */
169
+ export declare const recordHudUse: (use: HudUseData) => void;
165
170
  /** Tests only — drop the singleton so each case starts from zero. */
166
171
  export declare const resetSessionMetrics: () => void;
@@ -20,6 +20,7 @@ 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
+ import { HudMetrics } from './hud-metrics.js';
23
24
  /** Cap the distinct error shapes held in memory — a pathological loop must not grow unbounded. */
24
25
  const MAX_ERROR_KINDS = 40;
25
26
  /** Distinct tool names whose repeat-runs are tracked. Its own cap: the tool surface, not error shapes. */
@@ -165,10 +166,16 @@ export class SessionMetrics {
165
166
  #surface;
166
167
  #startedAt;
167
168
  #now;
169
+ #hud;
168
170
  /** Clock injected — this file must stay testable without a real one, per the repo's clock rule. */
169
171
  constructor(now) {
170
172
  this.#now = now;
171
173
  this.#startedAt = now();
174
+ this.#hud = new HudMetrics(now);
175
+ }
176
+ /** A person used the HUD. Names only; the wire schema has already narrowed it. */
177
+ recordHudUse(use) {
178
+ this.#hud.record(use);
172
179
  }
173
180
  /**
174
181
  * Record a call starting. Returns a function to call when it finishes — that closure is what makes
@@ -555,6 +562,7 @@ export class SessionMetrics {
555
562
  ...(this.#firstAppAt === undefined
556
563
  ? {}
557
564
  : { msToFirstApp: Math.max(0, this.#firstAppAt - this.#startedAt) }),
565
+ ...this.#hud.summarize(),
558
566
  final,
559
567
  // Only on a real exit. A periodic flush carrying `exit: "unknown"` would read as a daemon that
560
568
  // died without a shutdown path, which is the one thing this field exists to make visible.
@@ -613,7 +621,8 @@ export class SessionMetrics {
613
621
  0 === this.#bugsFound &&
614
622
  0 === this.#sdkFailures &&
615
623
  0 === this.#postSocketFailures &&
616
- 0 === this.#postRetriesSaved);
624
+ 0 === this.#postRetriesSaved &&
625
+ this.#hud.empty);
617
626
  }
618
627
  /** Zero the counters after a non-final flush so the next flush reports the NEXT window, not a total. */
619
628
  reset() {
@@ -641,6 +650,7 @@ export class SessionMetrics {
641
650
  this.#unknownToolCalls = 0;
642
651
  this.#bugsFound = 0;
643
652
  this.#bugKinds.clear();
653
+ this.#hud.reset();
644
654
  // #seenBugKinds is deliberately NOT cleared. It is not a window counter — it is the
645
655
  // session-lifetime memory behind `repeat` on bug_found, and zeroing it made the same defect,
646
656
  // found again after a flush, report as a newly distinct one. Sessions run to 11.5 hours in the
@@ -665,6 +675,10 @@ export const recordBrowserLatency = (ms) => {
665
675
  export const recordSdkFailure = (failure) => {
666
676
  getSessionMetrics().recordSdkFailure(failure.site, failure.message);
667
677
  };
678
+ /** Narrow-and-record one HUD use. Keeps the session hot path to a single call. */
679
+ export const recordHudUse = (use) => {
680
+ getSessionMetrics().recordHudUse(use);
681
+ };
668
682
  /** Tests only — drop the singleton so each case starts from zero. */
669
683
  export const resetSessionMetrics = () => {
670
684
  current = undefined;
@@ -10,9 +10,10 @@
10
10
  * Kept in one place, out of `runTool`, because a rule this easy to get wrong should be readable and
11
11
  * testable on its own rather than inferred from a ternary inside a dispatcher.
12
12
  */
13
- import { CaptureLoss, VerifiedReason } from '@reticlehq/core';
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 { harnessDriving } from './harness-drive.js';
16
17
  /**
17
18
  * The deciding clause, read off the result `decideVerified` wrote it onto.
18
19
  *
@@ -51,6 +52,30 @@ const SuiteStatus = {
51
52
  PASS: 'pass',
52
53
  FAIL: 'fail',
53
54
  };
55
+ /**
56
+ * A flow replay's verdict, in the vocabulary the rest of this file speaks.
57
+ *
58
+ * `ReplayStatus` spells itself `ok | drift | error`, so a reader looking for `pass`/`fail` found
59
+ * neither and every replay emitted nothing at all — on the tool documented as the single-flow
60
+ * companion to the whole-suite replay, i.e. a verdict in no numerator and no denominator.
61
+ *
62
+ * `error` maps to UNKNOWN rather than to a fail. It is the flow failing to LOAD or an action failing
63
+ * to run, which is Reticle not being able to tell — publishing that as the app's defect is exactly
64
+ * the mistake the suite reader made the first time.
65
+ *
66
+ * Read for the REPLAY TOOL ONLY. `ok` is not a verdict word anywhere else in this surface, and a
67
+ * merged action that happens to answer with one must not be counted as a verification it never made.
68
+ */
69
+ const REPLAY_VERDICTS = new Map([
70
+ [ReplayStatus.OK, { verified: Verified.YES, passed: true }],
71
+ [ReplayStatus.DRIFT, { verified: Verified.NO, passed: false }],
72
+ [ReplayStatus.ERROR, { verified: Verified.UNKNOWN, passed: false }],
73
+ ]);
74
+ function replayVerdict(toolName, status) {
75
+ if (ReticleTool.FLOW_REPLAY !== toolName || 'string' !== typeof status)
76
+ return undefined;
77
+ return REPLAY_VERDICTS.get(status);
78
+ }
54
79
  /**
55
80
  * Did the assertion itself pass, across three tool shapes that spell it three ways.
56
81
  *
@@ -97,13 +122,14 @@ export function verificationOf(toolName, result, durationMs, brand) {
97
122
  // here for work that happened.
98
123
  if (true === result['paused'])
99
124
  return undefined;
100
- const verified = 'string' === typeof result['verified'] ? result['verified'] : undefined;
125
+ const replay = replayVerdict(toolName, result['status']);
126
+ const verified = 'string' === typeof result['verified'] ? result['verified'] : (replay?.verified ?? undefined);
101
127
  // flow_verify reports `status: pass|fail|unverifiable`; assert reports a boolean `pass`. Accept
102
128
  // either shape so the whole family is covered without normalizing four tools' contracts for a
103
129
  // metric's convenience — but a status that is neither pass nor fail is NOT a verdict, and an empty
104
130
  // suite reports exactly that.
105
131
  const status = result['status'];
106
- const passed = passedOf(result, status);
132
+ const passed = passedOf(result, status) ?? replay?.passed;
107
133
  if (verified === undefined && passed === undefined)
108
134
  return undefined;
109
135
  const reason = reasonOf(result);
@@ -142,5 +168,7 @@ export function verificationOf(toolName, result, durationMs, brand) {
142
168
  // WHAT was lost, when the reason was that something was. Three owners, three fixes, one bar
143
169
  // until now.
144
170
  ...(uncleanLoss === undefined ? {} : { uncleanLoss }),
171
+ // WHOSE drive this was. Absent is the ordinary case — the agent called the tool itself.
172
+ ...(harnessDriving() ? { driven: VerificationDriver.HARNESS } : {}),
145
173
  };
146
174
  }
@@ -0,0 +1,31 @@
1
+ import { Bridge } from './portal/bridge/bridge.js';
2
+ import { FlowStore } from './language/flows/flows.js';
3
+ import type { FileSystemPort } from './memory/project/fs/fs-port.js';
4
+ import type { TapeStep } from './memory/journal/drive-flow.js';
5
+ import type { OnboardingStep } from '@reticlehq/core/telemetry';
6
+ import { type PruneWorkspaceOptions } from './memory/journal/on-disk/startup-maintenance.js';
7
+ /**
8
+ * Wire journal capture, ambient seeding and the journal-tail flush onto a bridge.
9
+ *
10
+ * Both entry points need all three, and both used to hand-roll them. `startDaemon` only ever wired the
11
+ * first, so on the path every user actually takes (`reticle serve` / `reticle mcp`) the journal tail was
12
+ * dropped at session end and the learned ambient map was never persisted OR seeded — meaning ambient
13
+ * learning could not converge across sessions and the last events of every session were lost. The two
14
+ * call sites had already drifted once before, which is why this is one function rather than a
15
+ * copy-paste both are asked to keep in step.
16
+ */
17
+ export declare function attachJournal(bridge: Bridge, deps: {
18
+ fs: FileSystemPort;
19
+ reticleRoot: string;
20
+ enabled: boolean;
21
+ takeAmbientTape?: () => {
22
+ steps: readonly TapeStep[];
23
+ startPath?: string;
24
+ } | undefined;
25
+ reportStep?: (step: OnboardingStep) => Promise<boolean>;
26
+ /** Tell cloud sync a run landed, so it cycles instead of waiting for its timer. */
27
+ onRunPersisted?: () => void;
28
+ flows?: FlowStore;
29
+ /** What this project is willing to keep — the `retain` block of its `.reticle.json`. */
30
+ retain?: PruneWorkspaceOptions;
31
+ }): void;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Journal wiring for the composition root.
3
+ *
4
+ * A sibling of `index.ts` rather than a move into `memory/journal/`: this is COMPOSITION, not
5
+ * journalling. It reaches the bridge, the flow store, the telemetry funnel and the recording tape to
6
+ * join them together, and putting those reaches inside the journal domain would make that domain
7
+ * depend on four areas it has no business knowing. `index.ts` already holds every one of them, and
8
+ * the 1000-line backstop is what asked for the split.
9
+ */
10
+ import { artifactRootResolver } from './memory/project/artifact-root-resolver.js';
11
+ import { originOf } from './portal/session/session-manager.js';
12
+ import { Bridge } from './portal/bridge/bridge.js';
13
+ import { FlowStore } from './language/flows/flows.js';
14
+ import { makeJournalAttach } from './memory/journal/attach-journal.js';
15
+ import { makeSessionEnd, recordDriveRun } from './memory/journal/session-end.js';
16
+ import { attachDriveRunFlush } from './memory/journal/drive-run-flush.js';
17
+ import { AmbientStore } from './memory/journal/ambient-store.js';
18
+ import { ensureWorkspaceGitignore } from './memory/journal/on-disk/workspace-gitignore.js';
19
+ import { pruneWorkspace, } from './memory/journal/on-disk/startup-maintenance.js';
20
+ import { dirname } from 'node:path';
21
+ import { findProjectConfig } from './command/cli/ports/resolve/cli-port.js';
22
+ import { readBackgroundTraffic } from './portal/session/facts/background-traffic.js';
23
+ /**
24
+ * Wire journal capture, ambient seeding and the journal-tail flush onto a bridge.
25
+ *
26
+ * Both entry points need all three, and both used to hand-roll them. `startDaemon` only ever wired the
27
+ * first, so on the path every user actually takes (`reticle serve` / `reticle mcp`) the journal tail was
28
+ * dropped at session end and the learned ambient map was never persisted OR seeded — meaning ambient
29
+ * learning could not converge across sessions and the last events of every session were lost. The two
30
+ * call sites had already drifted once before, which is why this is one function rather than a
31
+ * copy-paste both are asked to keep in step.
32
+ */
33
+ export function attachJournal(bridge, deps) {
34
+ const journalAttach = makeJournalAttach(deps);
35
+ const ambientStore = new AmbientStore(deps.fs, deps.reticleRoot);
36
+ // Built once, not per session: the resolver walks config discovery and the user-level registry,
37
+ // and neither changes between two tabs connecting a second apart.
38
+ const resolveArtifactRoot = artifactRootResolver(deps.reticleRoot);
39
+ bridge.attachSessionCreate((session) => {
40
+ // Stamp the project's own `.reticle` before ANY counter fires for this session. Without it every
41
+ // verdict is recorded against wherever the daemon was started, which is how one app's evidence
42
+ // reached a different account's production dashboard.
43
+ // The origin is passed for the case where the page never stamped a project id: it is the only
44
+ // distinguishing fact left, and without it every such app shares one bucket.
45
+ session.artifactRoot = resolveArtifactRoot(session.projectId, originOf(session.url)).root;
46
+ // The project's config sits beside its `.reticle/`, so the declaration is read from the tab's
47
+ // own project rather than from wherever the daemon was started.
48
+ session.background = readBackgroundTraffic(findProjectConfig(dirname(session.artifactRoot)));
49
+ // Here rather than on the start path, which was neither the moment we were about to write into a
50
+ // repository nor the root we were about to write into: it created `.reticle/` — holding nothing
51
+ // but the ignore file — wherever the daemon was launched, coming back every boot after the user
52
+ // deleted it, while the journals this ignore protects landed in another tree, uncovered.
53
+ if (deps.enabled)
54
+ void ensureWorkspaceGitignore(deps.fs, session.artifactRoot);
55
+ journalAttach(session);
56
+ // Seed the learned ambient map so a fresh session starts knowing which regions churn, instead of
57
+ // re-learning from zero. Best-effort + async: a late seed still helps, a failure is silent.
58
+ if (deps.enabled) {
59
+ void ambientStore
60
+ .load()
61
+ .then((counts) => session.seedAmbient(counts))
62
+ .catch(() => undefined);
63
+ }
64
+ });
65
+ // Teardown: flush the journal tail to disk + persist what this session learned.
66
+ bridge.attachSessionEnd(makeSessionEnd({
67
+ ...deps,
68
+ ...(deps.retain === undefined ? {} : { retain: deps.retain }),
69
+ // Retention runs from teardown, and it must not delete the journal of a session that is still
70
+ // being written. The registry is the only thing that knows which those are.
71
+ liveSessionIds: () => new Set(bridge.sessions.all().map((s) => s.id)),
72
+ }));
73
+ /*
74
+ * And the same write, DURING the session rather than only at the end of it.
75
+ *
76
+ * Teardown was the only writer of a run artifact, so every verdict produced while a tab stayed
77
+ * open was invisible to cloud sync — which can only push artifacts that exist. Reported as "sync
78
+ * is not happening" and diagnosed as the agent forgetting to sync; there is no sync command to
79
+ * forget. The evidence simply was not on disk yet.
80
+ *
81
+ * Safe to call repeatedly because the run id is derived from the session, so this rewrites one
82
+ * artifact rather than accumulating them — a property `recordDriveRun` already had, for the
83
+ * unrelated reason that a reconnecting tab must not publish two overlapping rows.
84
+ */
85
+ if (deps.enabled) {
86
+ attachDriveRunFlush({
87
+ resolve: (sessionId) => bridge.sessions.get(sessionId),
88
+ write: (session) => recordDriveRun(deps, session),
89
+ });
90
+ }
91
+ // One call, because "what maintenance runs at startup" needs one answer. Inlined here, the byte
92
+ // budget was simply missing — written, tested, and called by nothing, while the three count
93
+ // bounds beside it ran every time. See startup-maintenance.ts.
94
+ //
95
+ // Deliberately NOT behind `deps.enabled`. Switching journalling off is what somebody does BECAUSE
96
+ // the directory got too big, and both sweep sites were gated on it, so that setting was the one
97
+ // under which nothing ever deleted what was already there. Visual diffs, feedback copies and run
98
+ // artifacts do not need the journal to be written at all and kept accumulating regardless.
99
+ void pruneWorkspace(deps.fs, deps.reticleRoot, new Set(bridge.sessions.all().map((s) => s.id)), deps.retain ?? {});
100
+ }
package/docs/README.md CHANGED
@@ -72,6 +72,7 @@ Everything here is published to [docs.reticle.sh](https://docs.reticle.sh) by `d
72
72
  | [vs-playwright-mcp.mdx](vs-playwright-mcp.mdx) | outside-in vs inside-out, and when Playwright is right |
73
73
  | [vs-chrome-devtools-mcp.mdx](vs-chrome-devtools-mcp.mdx) | cheaper per look, catches less: the trade, measured |
74
74
  | [vs-screenshots.mdx](vs-screenshots.mdx) | why a better vision model does not fix a non-visual bug |
75
+ | [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 |
75
76
  | [telemetry.md](telemetry.md) | what is collected, and how to turn it off |
76
77
  | [local-registry.md](local-registry.md) | installing an unpublished build |
77
78
  | [license-activation.mdx](license-activation.mdx) | you have a key: where to set it, how to confirm it, what to do when it fails |
@@ -97,7 +97,11 @@ A claim is real only when the layers agree. Check more than the UI:
97
97
 
98
98
  > **Rule:** a passing UI assert that the store, network, or signal contradicts is a **false green**.
99
99
 
100
- **Session health is universal.** Every live-session tool result carries a `session` block (`throttled`, `focused`, `lastSeenMs`); when `throttled:true` it also adds a `warning` + `recommendation` (refocus, or `reticle drive`). A throttled/backgrounded tab can silently no-op timers/rAF/pointer gestures. If you see `session.throttled`, distrust a green and refocus first.
100
+ **Session health is universal.** Every live-session tool result carries a `session` block (`throttled`, `focused`, `lastSeenMs`); when `throttled:true` it also adds a `warning` + `recommendation`. Read the `recommendation`: it differs by cause, and the two causes have different fixes.
101
+
102
+ `throttled` is `hidden || stale`, and it is not a verdict that the tab cannot be driven. A tab that is **hidden** does clamp timers and rAF, so an action can land on a page that never advances. A tab that is merely **stale**, meaning no health heartbeat arrived inside the window, is usually just a quiet page, and is very often still driveable; a session flagged this way has taken a sign-in and two clean net-grade verdicts with no retries. Try the drive before you conclude anything, and reach for `refuseWhenThrottled: true` when the work is timing-sensitive (animations, debounces, gestures), which is what actually degrades.
103
+
104
+ **Leasing is not a free upgrade.** `reticle_lease` gives a guaranteed scriptable context, and it is a _separate_ one: the HUD lives in the human's tab, so everything you do in a lease is invisible to the person watching. Taking that advice with someone sitting in front of the app makes the product look broken while it works correctly. Working alone, lease and do not think about it; with an audience, spend a slower tab to stay visible.
101
105
 
102
106
  > Store reads (`reticle_look { action: "state" }`) are the reliable path; the DOM can lie (optimistic UI, stale render).
103
107
 
@@ -109,7 +113,7 @@ A claim is real only when the layers agree. Check more than the UI:
109
113
 
110
114
  ## Core tool set
111
115
 
112
- The nine tools you are shown. There is no `reticle_run` on this surface: these nine plus their actions are the whole of what you can call.
116
+ The tools you are shown, plus `reticle_run`, which calls any registered tool by name whether or not it is advertised.
113
117
 
114
118
  `reticle_navigate` · `reticle_act` · `reticle_act_and_wait` · `reticle_assert` · `reticle_look` · `reticle_observe` · `reticle_session` · `reticle_verify` · `reticle_tools`
115
119
 
@@ -168,4 +172,4 @@ Recent additions, each of which answers a question agents were previously asking
168
172
  - **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.
169
173
  - **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.
170
174
  - **Predicate schema is not bloated.** The recursive predicate DSL used by `reticle_assert` / `reticle_assert { action: "wait" }` / `reticle_act_and_wait` is **factored, not inlined**: when converted to the JSON Schema MCP sends, the predicate body is emitted **once** (~2.7k chars ≈ **~685 tokens** per tool) and recursion is handled by self-`$ref` (`#/properties/predicate`), with no per-recursion duplication. No action needed.
171
- - **One tool surface, and it is closed.** Reticle advertises nine tools: navigate/act/act_and_wait/assert/look/observe/session/verify, plus `reticle_tools`. 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 tool name and summary, `names:[…]` loads full params on demand. There is **no `reticle_run`** here, so a tool that is not in the nine is not reachable by any call: recording a flow, screenshots, visual diff, storage, network mocking and the clock all need a daemon started with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read at startup, so restart it), which advertises the full table with output schemas. `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.
175
+ - **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 the full table outright, with output schemas. `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.
package/docs/autodrive.md CHANGED
@@ -28,16 +28,56 @@ A drive costs a model. A **saved flow costs nothing to run again**: `reticle_ver
28
28
 
29
29
  So the drive is a one-off: pay for it once, and the journeys it recorded become the regression suite every later run replays. That is why the harness is told to record as it goes, and why a drive that saved nothing is reported as having proved nothing, however long it ran.
30
30
 
31
+ ## It replays before it drives
32
+
33
+ A drive reads `.reticle` first: every saved flow with the consequence that must still hold, and every signal and testid your app declares that no flow has ever asserted. It builds a plan from that before it opens its eyes on a page.
34
+
35
+ Anything already recorded is **replayed**, deterministically, with no model call at all. Only the gaps are driven. So a second run over the same app is mostly free, and the model budget is spent on the part nobody has proved yet rather than on rediscovering what is already on disk. A replay that goes red is reported as a regression; one that DRIFTS is reported separately, because the app moved under the recording and that needs re-anchoring rather than a code change.
36
+
37
+ Every journey a drive walks is saved, whatever happens to the drive, including one that broke, ran out of budget, or whose model simply stopped asking for tools.
38
+
31
39
  ## What it needs
32
40
 
33
- `ANTHROPIC_API_KEY` in the **daemon's** environment. Without it the action refuses and says so; every other part of Reticle is unaffected. Two other dials, both optional:
41
+ **A model in the daemon's environment.** Any one of three, and the choice is reported back so a comparison can never mislabel its own arms:
42
+
43
+ | Variable | Driver |
44
+ | --- | --- |
45
+ | `RETICLE_API_KEY` | `jev`, against the platform's own proxy, using the key `reticle link` already wrote. No model API key of your own, and the cheap path: it answers typed questions about the page rather than generating tool calls as text. |
46
+ | `ANTHROPIC_API_KEY` | `anthropic`, which generates its calls as text. |
47
+ | `OPENAI_API_KEY` | `openai`, the same, on OpenAI. |
48
+ | `JEV_API_KEY` | `jev`, against TypeSafe directly, for anyone who holds their own key. |
49
+
50
+ With several configured, `anthropic` is the default and `RETICLE_HARNESS_DRIVER` moves off it; with only a platform key, `jev` is not the cheaper option, it is the only one. Naming a driver that is not configured is an error, never a silent substitution.
51
+
52
+ A driver can also be chosen per call, as `reticle_verify { action: "explore", driver: "jev" }`. The question people actually have is comparative, and answering it with an environment variable means restarting the daemon between arms.
53
+
54
+ Three other dials, all optional:
34
55
 
35
56
  | Variable | What it does |
36
57
  | --- | --- |
37
- | `RETICLE_HARNESS_MODEL` | The model that drives. Defaults to a mid-tier one on purpose, for the reason below. |
58
+ | `RETICLE_HARNESS_MODEL` | The Anthropic model that drives. Defaults to a mid-tier one on purpose, for the reason below. |
38
59
  | `RETICLE_HARNESS_MAX_STEPS` | Ceiling on model turns in one drive. Bounds cost, not value. |
39
60
  | `RETICLE_HARNESS_BASE_URL` | A proxy or gateway instead of the default API host. |
40
61
 
62
+ ## Two models, and what each one is for
63
+
64
+ The `jev` driver decides; it cannot write. Every choice a drive makes is a selection from candidates Reticle enumerated off the page: which element, which tool, what consequence to claim. That is why it costs a fraction of a generating model and answers in a few hundred milliseconds.
65
+
66
+ One thing in a drive is not a selection. A text field is a composition: "a business name", "a statement descriptor", "a search term that returns results" cannot be enumerated from the page. Where the field's label is enough to guess, a small table answers it. Where it is not, and a generating model is configured, that model writes the value, and nothing else.
67
+
68
+ Typing is a small minority of what a drive does: clicks and navigations are the overwhelming majority. So this is escalation rather than a second model in the loop, and a drive with no text fields never calls it at all.
69
+
70
+ What it writes is a FIXTURE, not an opinion. Generated values are saved to `.reticle/fill-values.json` and reused forever: the second drive pays nothing, a replay sends exactly what the recording sent, and a value you dislike is a line in a git-checked file you can edit rather than an argument with a model.
71
+
72
+ ## Turning it off, and who pays for it
73
+
74
+ A project linked to a Reticle workspace reads two things from it before a drive starts, and honours both:
75
+
76
+ - **The switch.** Autonomous driving can be turned off per project in the dashboard, under Settings → Verification model. A drive then refuses and says where to turn it back on. Everything else is unaffected, including the tools your own agent drives with.
77
+ - **Who is paying.** Driving through the platform spends Reticle's model budget, which is free for three months once claimed and included on a paid plan. Outside both, a drive is refused with the claim link rather than run on somebody else's money.
78
+
79
+ Neither applies to a drive on a model key of your own: that costs us nothing, so it is not ours to gate. A daemon that cannot reach the platform at all drives normally: an unreachable settings endpoint is not a reason to lose a feature you were never told to stop using.
80
+
41
81
  ## What it will not do
42
82
 
43
83
  **It does not decide whether your app is correct.** The model chooses what to _try_; the engine decides what _happened_, from what it recorded. A model that graded its own driving would be scoring its own homework, and its verdict would be unfalsifiable. So the summary it writes is an account of what it drove, and nothing downstream grades from it.
@@ -71,7 +71,7 @@ Why not compare to traditional test frameworks (Playwright the library, Cypress,
71
71
 
72
72
  There **is** a comparison against a Playwright script, and it answers a different question: not "what does it cost an agent to look" but "what can each tool structurally SEE, at all." That one is in Part 5a. It runs no model, so nothing about it depends on how well an agent drives, which is what makes it the more durable of the two results.
73
73
 
74
- Tool versions are pinned in `bench/raw/run-meta.json` so any run is reproducible.
74
+ Tool versions are recorded by hand in `bench/README.md`, not generated: the file that used to pin them is no longer produced. Treat that table as the expiry date on every cross-tool number here, and re-derive before quoting rather than assuming a run from months ago still describes today's tools.
75
75
 
76
76
  ---
77
77
 
@@ -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 on the default tool surface, and the default surface has no `reticle_run` to reach them**: start the daemon with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read once at startup, so restart it) and call them by name. The `reticle_run({ tool, args })` spelling on this page works only on that 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. To have them advertised outright instead, with their output schemas, start the daemon with `RETICLE_ADVERTISE_ALL_TOOLS=1` (read once at startup, so restart it) and call them by name.
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
 
@@ -20,7 +20,7 @@ These are thin clients over the `/v1` API. The logic is server-side; these verbs
20
20
  | `~/.reticle/credentials.json` | Project-scoped API keys from `reticle link`, keyed by `projectId` | Yes |
21
21
  | `<repo>/.reticle/cloud.json` | `{ projectId, projectName, url, sync, verify }`. The non-secret binding | No, commit it |
22
22
 
23
- Auth for any command is `RETICLE_CLOUD_KEY` from the environment (the agent path) if set, otherwise the login token. `RETICLE_CLOUD_URL` overrides the endpoint; the default is `http://localhost:8890`. When a command falls back to that default without `RETICLE_CLOUD_URL` (or a saved session URL), it prints a one-line notice on stderr; stdout stays pure JSON for agents.
23
+ Auth for any command is `RETICLE_API_KEY` from the environment (the agent path) if set, otherwise the login token. `RETICLE_CLOUD_URL` overrides the endpoint; the default is `http://localhost:8890`. When a command falls back to that default without `RETICLE_CLOUD_URL` (or a saved session URL), it prints a one-line notice on stderr; stdout stays pure JSON for agents.
24
24
 
25
25
  ## The commands
26
26
 
@@ -70,7 +70,7 @@ An unattached repo also gets a next-step nudge on stderr, telling you to run `re
70
70
  npx @reticlehq/server link [--project <name|id>]
71
71
  ```
72
72
 
73
- Binds this repository to a cloud project. With a login token it **mints** a project-scoped key, so there is nothing to paste. With `RETICLE_CLOUD_KEY` already set, it resolves that key's project instead. `--project` accepts a slug id or a display name; omitted, it uses the `default` project. Writes `cloud.json` and the key.
73
+ Binds this repository to a cloud project. With a login token it **mints** a project-scoped key, so there is nothing to paste. With `RETICLE_API_KEY` already set, it resolves that key's project instead. `--project` accepts a slug id or a display name; omitted, it uses the `default` project. Writes `cloud.json` and the key.
74
74
 
75
75
  ### `reticle project`
76
76
 
@@ -101,7 +101,9 @@ reticle: no .reticle/cloud.json here ... run `reticle link` first
101
101
 
102
102
  ### `reticle push`
103
103
 
104
- Sends local run artifacts from `.reticle` to the linked project, honoring the sync policy. With `sync.runs` off it does nothing and says so. Prints `{ pushed, failed, total, project }`.
104
+ Sends local run artifacts from `.reticle` to the linked project, honoring the sync policy. With `sync.runs` off it does nothing and says so. Prints `{ ok, project, sent, pulled }`, where `sent` carries the accepted counts and, when the server refused anything, a `rejected` array naming each artifact's index and reason.
105
+
106
+ **A rejection is a failed push.** If the server refuses any artifact, `ok` is `false` and the command exits `1`, even when other artifacts in the same bundle landed. A refused artifact never reaches the dashboard and is re-offered on every cycle, so partial loss is still loss. `ok: true` and exit `0` mean everything offered was accepted, which includes the common case of a repo that was already up to date. The accepted counts still report what did land, so a partial push is visible as one.
105
107
 
106
108
  ### `reticle runs`
107
109
 
@@ -133,12 +135,12 @@ reticle: cloud not attached here ... run `reticle link` (or set RETICLE_CLOUD_UR
133
135
 
134
136
  ## Exit codes
135
137
 
136
- | Code | Meaning |
137
- | ---- | ----------------------------------------------------------------------------------- |
138
- | `0` | Success |
139
- | `1` | The API call failed, or the repository is not attached where attachment is required |
140
- | `2` | Usage error, or not signed in and no `RETICLE_CLOUD_KEY` |
141
- | `3` | `regression` only: at least one flow broke |
138
+ | Code | Meaning |
139
+ | --- | --- |
140
+ | `0` | Success |
141
+ | `1` | The API call failed, the repository is not attached where attachment is required, or `push` had an artifact refused |
142
+ | `2` | Usage error, or not signed in and no `RETICLE_API_KEY` |
143
+ | `3` | `regression` only: at least one flow broke |
142
144
 
143
145
  Output is pretty-printed JSON on stdout. Errors and next-step nudges go to stderr, so an agent parsing stdout is unaffected.
144
146
 
@@ -52,6 +52,7 @@ When nothing has connected, the `sessions` line fails and the daemon's own diagn
52
52
  | `daemon` | Whether the port holds a Reticle daemon, a foreign process (named where possible), or nothing. Adds a `version` line when the daemon's version or contract fingerprint disagrees with the CLI's |
53
53
  | `sessions` | Whether any page has actually connected. Zero is a FAILING check rather than a neutral note, because it is the state the setup most often stalls in: wired correctly, daemon up, nothing dialled in. When it is zero the daemon's own no-session diagnosis prints below the checklist |
54
54
  | `agent link` | Whether an MCP client has listed Reticle's tools on this port **and actually called one**. Every other row here checks a component; this one checks the hop between your agent and the daemon. It is possible for the SDK to be injected, a session to be live, and your client to show every tool enabled, while no request has ever crossed from the agent. That state passes every other check and is the one people lose hours to |
55
+ | `loopback` | Which of `localhost`'s two answers reaches the bridge. Printed **only when they disagree**. The daemon binds `127.0.0.1`, and a best-effort alias forwards `[::1]` to it so the name works everywhere. When that alias cannot open (IPv6 disabled, or something already holding `[::1]` on the port) the daemon serves IPv4 perfectly and a page told to dial `localhost` still cannot reach it, because Windows Chrome tries the IPv6 answer first. Every other row here goes green in that state |
55
56
  | `bridge port` | The port your app must dial, spelled out because mixing it up with the dev server port is the most common setup failure |
56
57
  | `port check` | Only when your project's configured port disagrees with the one the daemon is on. Prints the mismatch rather than leaving you to spot it across two lines |
57
58
  | `sibling` | Only when a well-known Reticle port other than this daemon's has a listener. An observation, not a diagnosis: that listener may or may not be related |
package/docs/cli/init.mdx CHANGED
@@ -19,6 +19,7 @@ npx @reticlehq/server init [--dry-run] [--port N] [--no-mcp] [--no-install] [--a
19
19
  | `--no-mcp` | boolean | `false` (MCP is registered) | Skip the MCP registration **and** the agent rule files (`CLAUDE.md`, `AGENTS.md`, `.cursor`) and the `/reticle` command |
20
20
  | `--no-install` | boolean | `false` (deps are installed) | Do not run the install. The step becomes `[⚠]` and prints the `add -D` command for you to run by hand, rather than vanishing from the plan |
21
21
  | `--app <dir>` | string | unset | Pick which app to wire in a monorepo, without changing directory |
22
+ | `--hooks` | boolean | `false` | Also add a print-only Stop hook to `.claude/settings.json`, merged into what is there. When the agent stops without a claim that held, it prints one line; it never blocks. See [`reticle report`](/cli/report) |
22
23
  | `--yes` | boolean | `false` | Accepted for scripting. `init` has no interactive prompts today, so it is a no-op |
23
24
 
24
25
  <Warning>
@@ -0,0 +1,44 @@
1
+ ---
2
+ title: reticle report
3
+ description: What the latest session claimed, and what held. Also a print-only Stop hook for Claude Code.
4
+ icon: clipboard-check
5
+ ---
6
+
7
+ `reticle report` reads this project's most recent session from `.reticle/` and says how many of its claims held, how many failed, how many came back `unknown` and who can act on each, and how many proved nothing. It reads the journal on disk, so no daemon has to be running.
8
+
9
+ ```bash
10
+ npx @reticlehq/server report [--session <id>] [--hook]
11
+ ```
12
+
13
+ ## Arguments and flags
14
+
15
+ | Flag | Default | What it does |
16
+ | --- | --- | --- |
17
+ | `--session <id>` | the newest session | Report a specific session |
18
+ | `--hook` | off | Print one line, and only when the working tree changed and no claim held |
19
+
20
+ ## What it prints
21
+
22
+ ```text
23
+ session lease-85f8191b-3874-457b-973b-8ee365e5acfb
24
+ 2 of 4 claims held
25
+ 1 failed, 1 of them a green a channel contradicted
26
+ 1 unknown (environment: 1)
27
+ ✗ signal order:placed
28
+ ```
29
+
30
+ An `unknown` is never counted as held. It is Reticle saying it could not tell, and the owner beside it says what to do next.
31
+
32
+ ## As a Claude Code Stop hook
33
+
34
+ With `--hook` it prints nothing when a claim held or nothing in the tree changed, and otherwise one line, such as `Reticle: not verified. 3 claims: 1 failed, 2 unknown`. It prints Claude Code's `systemMessage` shape and always exits `0`: you see the line when the agent stops, and the agent is never blocked by it. `reticle init --hooks` adds it for you, merged into whatever `.claude/settings.json` already holds. By hand:
35
+
36
+ ```json
37
+ {
38
+ "hooks": {
39
+ "Stop": [{ "hooks": [{ "type": "command", "command": "npx @reticlehq/server report --hook" }] }]
40
+ }
41
+ }
42
+ ```
43
+
44
+ A session last written more than an hour ago does not count as this turn, so a morning drive cannot answer for an afternoon edit. If you want the stop blocked until the change is verified, use [`reticle gate --hook`](/cli/gate) instead.
@@ -7,7 +7,7 @@ icon: heart-pulse
7
7
  `reticle status` is the command that tells you whether the install actually finished. Not a tick in a checklist: a connected app.
8
8
 
9
9
  ```bash
10
- npx @reticlehq/server status [--port N]
10
+ npx @reticlehq/server status [--port N] [--json]
11
11
  ```
12
12
 
13
13
  ## Flags
@@ -15,10 +15,37 @@ npx @reticlehq/server status [--port N]
15
15
  | Flag | Type | Default | What it does |
16
16
  | --- | --- | --- | --- |
17
17
  | `--port N` | number | `4400` (or `RETICLE_PORT`, or the port in `.reticle.json`) | Which daemon to ask |
18
+ | `--json` | flag | off | Print the machine-readable event instead of the block below |
18
19
 
19
20
  ## What it prints
20
21
 
21
- One JSON line on stdout. Real capture, daemon up and no app connected:
22
+ A short block, for whoever is reading the terminal. Real capture, daemon up with two apps connected:
23
+
24
+ ```text
25
+ reticle status
26
+ daemon ✓ running on :4400 (pid 90790)
27
+ sessions ✓ 2 pages connected
28
+ http://localhost:5173/ (throttled, hidden)
29
+ http://localhost:7699/ (throttled, hidden)
30
+ agent link enumerated
31
+ ```
32
+
33
+ And with no daemon on the port:
34
+
35
+ ```text
36
+ reticle status
37
+ daemon ✗ not running on :4999
38
+ agent link never-attached
39
+ next no daemon is running on this port, which is normal. An agent starts it when it
40
+ first calls a Reticle tool, and it exits again when idle. Ask your agent to verify
41
+ something, then run status again.
42
+ ```
43
+
44
+ A session's parenthesised notes are the ones that explain an answer you would otherwise call a bug: `throttled` and `hidden` mean a background tab answering slowly, `stale` means the page has moved on from what the daemon last saw, and `unresponsive` means it is not answering at all.
45
+
46
+ ### `--json`
47
+
48
+ The same facts as one event line on stderr, for a caller that parses rather than reads:
22
49
 
23
50
  ```json
24
51
  {
@@ -33,30 +60,18 @@ One JSON line on stdout. Real capture, daemon up and no app connected:
33
60
  }
34
61
  ```
35
62
 
36
- With an app connected, `why` is absent and each session carries its own fields:
63
+ It is one line in the terminal, pretty-printed here, with `why` trimmed at the ellipses. With an app connected `why` is absent and each session carries its own fields:
37
64
 
38
65
  ```json
39
66
  {
40
- "t": "2026-08-15T18:45:58.190Z",
41
- "event": "reticle_status",
42
- "port": 4400,
43
- "running": true,
44
- "pid": 72698,
45
- "sessionCount": 1,
46
- "sessions": [
47
- {
48
- "sessionId": "s223e0fad-e012-43f9-a23d-6d585470c90d",
49
- "url": "http://localhost:4310/",
50
- "throttled": false,
51
- "stale": false,
52
- "pendingMarks": 0
53
- }
54
- ]
67
+ "sessionId": "s223e0fad-e012-43f9-a23d-6d585470c90d",
68
+ "url": "http://localhost:4310/",
69
+ "throttled": false,
70
+ "stale": false,
71
+ "pendingMarks": 0
55
72
  }
56
73
  ```
57
74
 
58
- Both are one line of JSON in the terminal, pretty-printed here, with `why` trimmed at the ellipses.
59
-
60
75
  | Field | Meaning |
61
76
  | --- | --- |
62
77
  | `running` | Whether a usable daemon owns the port |