@reticlehq/server 3.2.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. package/LICENSE-ENTERPRISE +1 -1
  2. package/README.md +3 -3
  3. package/SKILL.md +23 -22
  4. package/dist/chromium-launch-options.d.ts +4 -3
  5. package/dist/chromium-launch-options.js +6 -2
  6. package/dist/command/cli/adhoc-suite.d.ts +41 -0
  7. package/dist/command/cli/adhoc-suite.js +159 -0
  8. package/dist/command/cli/adhoc-verdict.d.ts +58 -2
  9. package/dist/command/cli/adhoc-verdict.js +108 -13
  10. package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
  11. package/dist/command/cli/answers/gate-hook-message.js +6 -0
  12. package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
  13. package/dist/command/cli/auth/cloud-keystore.js +0 -3
  14. package/dist/command/cli/auth/cloud-session.d.ts +4 -0
  15. package/dist/command/cli/auth/cloud-session.js +2 -0
  16. package/dist/command/cli/cli-doctor.js +30 -8
  17. package/dist/command/cli/cli-flow-commands.d.ts +3 -6
  18. package/dist/command/cli/cli-flow-commands.js +67 -4
  19. package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
  20. package/dist/command/cli/cli-parse-grammar.js +7 -0
  21. package/dist/command/cli/cli-parse-verify.d.ts +2 -5
  22. package/dist/command/cli/cli-parse-verify.js +86 -13
  23. package/dist/command/cli/cli-parse.d.ts +14 -1
  24. package/dist/command/cli/cli-parse.js +54 -8
  25. package/dist/command/cli/cli-update-commands.js +8 -1
  26. package/dist/command/cli/cli-verify.d.ts +80 -0
  27. package/dist/command/cli/cli-verify.js +181 -26
  28. package/dist/command/cli/cloud-cli.js +5 -2
  29. package/dist/command/cli/cloud-kit.d.ts +0 -2
  30. package/dist/command/cli/cloud-kit.js +3 -5
  31. package/dist/command/cli/cloud-login.js +21 -3
  32. package/dist/command/cli/demo/demo-page.js +6 -3
  33. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
  34. package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
  35. package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
  36. package/dist/command/cli/launch/cli-launch.d.ts +9 -29
  37. package/dist/command/cli/launch/cli-launch.js +27 -49
  38. package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
  39. package/dist/command/cli/launch/sdk-version-match.js +206 -0
  40. package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
  41. package/dist/command/cli/ports/resolve/cli-port.d.ts +38 -0
  42. package/dist/command/cli/ports/resolve/cli-port.js +169 -15
  43. package/dist/command/cli/report-command.d.ts +16 -0
  44. package/dist/command/cli/report-command.js +97 -0
  45. package/dist/command/cli/setup-mcp-cli.js +13 -13
  46. package/dist/command/cli/tutorial.d.ts +2 -2
  47. package/dist/command/cli/tutorial.js +21 -7
  48. package/dist/command/cli.js +67 -10
  49. package/dist/command/daemon/binding/daemon-status-probe.d.ts +44 -2
  50. package/dist/command/daemon/binding/daemon-status-probe.js +79 -6
  51. package/dist/command/daemon/binding/port-presence.d.ts +1 -1
  52. package/dist/command/daemon/daemon-resolve.d.ts +9 -0
  53. package/dist/command/daemon/daemon-resolve.js +1 -1
  54. package/dist/command/daemon/idle-shutdown.d.ts +2 -1
  55. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
  56. package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
  57. package/dist/command/dev/stale-issue-guard.d.ts +3 -4
  58. package/dist/command/dev/stale-issue-guard.js +8 -8
  59. package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
  60. package/dist/command/setup/bringup/connect-lease.js +38 -0
  61. package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
  62. package/dist/command/setup/bringup/dev-server-files.js +131 -0
  63. package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
  64. package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
  65. package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
  66. package/dist/command/setup/bringup/ensure-daemon.js +135 -16
  67. package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
  68. package/dist/command/setup/bringup/static-page-server.js +151 -0
  69. package/dist/command/setup/desktop-shape.d.ts +4 -0
  70. package/dist/command/setup/desktop-shape.js +18 -2
  71. package/dist/command/setup/init/init-host.js +8 -0
  72. package/dist/command/setup/init/init-port.d.ts +15 -0
  73. package/dist/command/setup/init/init-port.js +44 -0
  74. package/dist/command/setup/init/init-runtime.d.ts +1 -1
  75. package/dist/command/setup/init/init-runtime.js +39 -2
  76. package/dist/command/setup/node-effects.d.ts +27 -14
  77. package/dist/command/setup/node-effects.js +80 -44
  78. package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
  79. package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
  80. package/dist/command/setup/probe/page-probe.d.ts +13 -2
  81. package/dist/command/setup/probe/page-probe.js +18 -3
  82. package/dist/command/setup/run-setup.d.ts +26 -1
  83. package/dist/command/setup/run-setup.js +96 -13
  84. package/dist/command/setup/session-pick.d.ts +13 -0
  85. package/dist/command/setup/session-pick.js +20 -0
  86. package/dist/command/setup/setup-command.d.ts +2 -14
  87. package/dist/command/setup/setup-command.js +105 -31
  88. package/dist/command/setup/setup-mcp.js +3 -3
  89. package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
  90. package/dist/command/setup/terminal/wait-progress.js +17 -0
  91. package/dist/features/crawl/crawl-empty.js +1 -1
  92. package/dist/features/crawl/crawl-tools.d.ts +6 -0
  93. package/dist/features/crawl/crawl-tools.js +57 -0
  94. package/dist/features/crawl/crawl.d.ts +2 -0
  95. package/dist/features/crawl/crawl.js +171 -156
  96. package/dist/features/exhaust/app-coverage.d.ts +33 -0
  97. package/dist/features/exhaust/app-coverage.js +31 -0
  98. package/dist/features/exhaust/code-coverage.d.ts +47 -0
  99. package/dist/features/exhaust/code-coverage.js +77 -0
  100. package/dist/features/exhaust/exhaust-run.d.ts +33 -0
  101. package/dist/features/exhaust/exhaust-run.js +82 -0
  102. package/dist/features/exhaust/explorer.d.ts +103 -0
  103. package/dist/features/exhaust/explorer.js +195 -0
  104. package/dist/features/exhaust/failure-branches.d.ts +33 -0
  105. package/dist/features/exhaust/failure-branches.js +92 -0
  106. package/dist/features/exhaust/ledger.d.ts +202 -0
  107. package/dist/features/exhaust/ledger.js +252 -0
  108. package/dist/features/exhaust/session-fold.d.ts +31 -0
  109. package/dist/features/exhaust/session-fold.js +79 -0
  110. package/dist/features/exhaust/session-port.d.ts +36 -0
  111. package/dist/features/exhaust/session-port.js +102 -0
  112. package/dist/features/harness/drive-report.d.ts +0 -9
  113. package/dist/features/harness/drive-report.js +18 -0
  114. package/dist/features/harness/driver.js +4 -7
  115. package/dist/features/harness/fill-values.d.ts +6 -0
  116. package/dist/features/harness/fill-values.js +3 -0
  117. package/dist/features/harness/jev-driver.js +36 -22
  118. package/dist/features/harness/openai-driver.js +4 -7
  119. package/dist/features/harness/platform-config.d.ts +10 -0
  120. package/dist/features/harness/platform-config.js +13 -6
  121. package/dist/index.d.ts +10 -1
  122. package/dist/index.js +78 -104
  123. package/dist/judgement/capsule/capsule-store.d.ts +31 -2
  124. package/dist/judgement/capsule/capsule-store.js +58 -6
  125. package/dist/judgement/capsule/capsule.js +5 -0
  126. package/dist/judgement/capsule/causal-summary.d.ts +13 -1
  127. package/dist/judgement/capsule/causal-summary.js +7 -0
  128. package/dist/judgement/domain/domain-model.js +40 -9
  129. package/dist/judgement/oracles/self-instrument.js +4 -2
  130. package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
  131. package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
  132. package/dist/judgement/runs/artifact/gap-report.js +46 -0
  133. package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
  134. package/dist/judgement/runs/artifact/gap-summary.js +85 -0
  135. package/dist/judgement/runs/artifact/render-report.js +4 -1
  136. package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
  137. package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
  138. package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
  139. package/dist/judgement/runs/artifact/to-artifact.js +1 -1
  140. package/dist/judgement/runs/context-tools.js +21 -3
  141. package/dist/judgement/runs/drive-run.js +7 -2
  142. package/dist/judgement/runs/verification-sync.d.ts +3 -1
  143. package/dist/judgement/runs/verification-sync.js +1 -1
  144. package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
  145. package/dist/language/flows/annotate-notes/annotate.js +66 -18
  146. package/dist/language/flows/change/affected.d.ts +9 -0
  147. package/dist/language/flows/change/affected.js +35 -0
  148. package/dist/language/flows/change/gate.d.ts +20 -0
  149. package/dist/language/flows/change/gate.js +12 -1
  150. package/dist/language/flows/flow-anchor.d.ts +12 -0
  151. package/dist/language/flows/flow-anchor.js +20 -0
  152. package/dist/language/flows/flow-author.d.ts +7 -0
  153. package/dist/language/flows/flow-author.js +19 -0
  154. package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
  155. package/dist/language/flows/flow-expect-grammar.js +92 -11
  156. package/dist/language/flows/flow-intent.js +1 -1
  157. package/dist/language/flows/flow-journey.d.ts +54 -0
  158. package/dist/language/flows/flow-journey.js +119 -0
  159. package/dist/language/flows/flow-learning.d.ts +3 -0
  160. package/dist/language/flows/flow-learning.js +39 -8
  161. package/dist/language/flows/flow-replay-run.d.ts +33 -12
  162. package/dist/language/flows/flow-replay-run.js +131 -52
  163. package/dist/language/flows/flow-replay-types.d.ts +9 -0
  164. package/dist/language/flows/flow-replay.d.ts +27 -12
  165. package/dist/language/flows/flow-replay.js +127 -111
  166. package/dist/language/flows/flow-result.d.ts +2 -0
  167. package/dist/language/flows/flow-result.js +25 -0
  168. package/dist/language/flows/flow-step-runners.d.ts +26 -10
  169. package/dist/language/flows/flow-step-runners.js +20 -4
  170. package/dist/language/flows/flow-success.d.ts +21 -6
  171. package/dist/language/flows/flow-success.js +50 -138
  172. package/dist/language/flows/flow-tools.js +17 -5
  173. package/dist/language/flows/flows.d.ts +18 -5
  174. package/dist/language/flows/flows.js +101 -16
  175. package/dist/language/flows/heal.d.ts +9 -1
  176. package/dist/language/flows/heal.js +24 -2
  177. package/dist/language/flows/learned-sources.d.ts +21 -0
  178. package/dist/language/flows/learned-sources.js +84 -0
  179. package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
  180. package/dist/language/flows/recording/recording-backtrack.js +1 -1
  181. package/dist/language/flows/recording/replay-halt.d.ts +2 -1
  182. package/dist/language/flows/recording/replay-halt.js +3 -1
  183. package/dist/language/flows/recording/tape/recordings.d.ts +31 -2
  184. package/dist/language/flows/recording/tape/recordings.js +55 -1
  185. package/dist/language/flows/replay.d.ts +19 -0
  186. package/dist/language/flows/replay.js +53 -13
  187. package/dist/language/flows/stores/annotation-store.d.ts +5 -5
  188. package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
  189. package/dist/language/flows/stores/flake-store.js +2 -3
  190. package/dist/language/flows/suite/coverage.d.ts +1 -1
  191. package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
  192. package/dist/language/flows/verify-change-tools.js +19 -2
  193. package/dist/launch-chromium.d.ts +80 -0
  194. package/dist/launch-chromium.js +218 -0
  195. package/dist/memory/cloud/cloud-config.d.ts +11 -2
  196. package/dist/memory/cloud/cloud-config.js +47 -7
  197. package/dist/memory/cloud/cloud-sync.d.ts +4 -3
  198. package/dist/memory/cloud/cloud-sync.js +7 -10
  199. package/dist/memory/cloud/harness-config.d.ts +2 -0
  200. package/dist/memory/cloud/harness-offer.js +5 -6
  201. package/dist/memory/cloud/harness-switch.js +5 -6
  202. package/dist/memory/cloud/sync-cycle.d.ts +78 -1
  203. package/dist/memory/cloud/sync-cycle.js +321 -53
  204. package/dist/memory/cloud/sync-daemon.d.ts +13 -0
  205. package/dist/memory/cloud/sync-daemon.js +45 -20
  206. package/dist/memory/cloud/sync-disk.js +27 -8
  207. package/dist/memory/impact/impact-store.js +6 -0
  208. package/dist/memory/intent/inline-intent.js +8 -3
  209. package/dist/memory/intent/intent-shard-store.d.ts +33 -47
  210. package/dist/memory/intent/intent-shard-store.js +252 -140
  211. package/dist/memory/intent/intent-shard.d.ts +0 -23
  212. package/dist/memory/intent/intent-shard.js +1 -11
  213. package/dist/memory/intent/intent-store.d.ts +13 -8
  214. package/dist/memory/intent/intent-store.js +46 -90
  215. package/dist/memory/intent/intent-tools.js +34 -23
  216. package/dist/memory/journal/ambient-store.js +2 -3
  217. package/dist/memory/journal/deviation-report.js +4 -1
  218. package/dist/memory/journal/deviation-service.js +10 -2
  219. package/dist/memory/journal/drive-flow.d.ts +19 -14
  220. package/dist/memory/journal/drive-flow.js +65 -48
  221. package/dist/memory/journal/envelope-store.js +2 -3
  222. package/dist/memory/journal/journal-recorder.d.ts +21 -0
  223. package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
  224. package/dist/memory/journal/on-disk/retain-policy.js +44 -0
  225. package/dist/memory/journal/on-disk/retention.d.ts +14 -10
  226. package/dist/memory/journal/on-disk/retention.js +13 -12
  227. package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
  228. package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
  229. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -11
  230. package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -45
  231. package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
  232. package/dist/memory/journal/on-disk/workspace-tiers.js +90 -0
  233. package/dist/memory/journal/route-template.d.ts +22 -0
  234. package/dist/memory/journal/route-template.js +44 -0
  235. package/dist/memory/journal/session-end.d.ts +13 -2
  236. package/dist/memory/journal/session-end.js +88 -18
  237. package/dist/memory/journal/session-journal.d.ts +8 -1
  238. package/dist/memory/journal/session-journal.js +10 -0
  239. package/dist/memory/project/baselines.d.ts +1 -1
  240. package/dist/memory/project/dir/reticle-dir.d.ts +3 -1
  241. package/dist/memory/project/dir/reticle-dir.js +1 -0
  242. package/dist/memory/project/fs/fs-port.js +44 -5
  243. package/dist/memory/project/fs/write-atomic.d.ts +26 -0
  244. package/dist/memory/project/fs/write-atomic.js +48 -0
  245. package/dist/memory/project/project-tools.js +3 -1
  246. package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
  247. package/dist/memory/recall/prior/connection-memory.js +8 -0
  248. package/dist/memory/recall/registered-projects.d.ts +2 -1
  249. package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
  250. package/dist/portal/bridge/auth-failure-reason.js +9 -4
  251. package/dist/portal/bridge/bridge.d.ts +8 -0
  252. package/dist/portal/bridge/bridge.js +43 -2
  253. package/dist/portal/input/js-coverage.d.ts +25 -0
  254. package/dist/portal/input/js-coverage.js +30 -0
  255. package/dist/portal/input/real-input.d.ts +11 -0
  256. package/dist/portal/input/real-input.js +31 -5
  257. package/dist/portal/pool/browser-pool.d.ts +10 -0
  258. package/dist/portal/pool/browser-pool.js +10 -0
  259. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  260. package/dist/portal/pool/playwright-launcher.js +21 -13
  261. package/dist/portal/pool/pool-contract.d.ts +2 -0
  262. package/dist/portal/pool/storage-seed.d.ts +2 -1
  263. package/dist/portal/pool/zero-install.d.ts +27 -0
  264. package/dist/portal/pool/zero-install.js +54 -0
  265. package/dist/portal/realm/web-realm.js +6 -0
  266. package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
  267. package/dist/portal/session/dev-server/dev-command.js +30 -12
  268. package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
  269. package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
  270. package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
  271. package/dist/portal/session/dev-server/route-status-probe.js +57 -0
  272. package/dist/portal/session/dev-server/served-document.d.ts +21 -0
  273. package/dist/portal/session/dev-server/served-document.js +57 -0
  274. package/dist/portal/session/facts/background-traffic.d.ts +9 -0
  275. package/dist/portal/session/facts/background-traffic.js +25 -0
  276. package/dist/portal/session/facts/observed-state.d.ts +11 -0
  277. package/dist/portal/session/facts/observed-state.js +34 -0
  278. package/dist/portal/session/live-control-tools.js +48 -3
  279. package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
  280. package/dist/portal/session/no-session-diagnosis.js +62 -5
  281. package/dist/portal/session/no-session-next-action.d.ts +13 -0
  282. package/dist/portal/session/no-session-next-action.js +48 -9
  283. package/dist/portal/session/no-session-watch.d.ts +18 -0
  284. package/dist/portal/session/no-session-watch.js +88 -2
  285. package/dist/portal/session/presence/attachment-history.d.ts +13 -2
  286. package/dist/portal/session/session-health.d.ts +61 -0
  287. package/dist/portal/session/session-health.js +43 -3
  288. package/dist/portal/session/session-info.d.ts +6 -0
  289. package/dist/portal/session/session-manager.d.ts +29 -6
  290. package/dist/portal/session/session-manager.js +42 -5
  291. package/dist/portal/session/session-reaper.d.ts +28 -0
  292. package/dist/portal/session/session-reaper.js +49 -1
  293. package/dist/portal/session/session-successor.d.ts +11 -0
  294. package/dist/portal/session/session-successor.js +14 -1
  295. package/dist/portal/session/session-verdict-facts.d.ts +28 -0
  296. package/dist/portal/session/session-verdict-facts.js +15 -0
  297. package/dist/portal/session/session.d.ts +10 -0
  298. package/dist/portal/session/session.js +34 -26
  299. package/dist/surface/http-server.d.ts +2 -1
  300. package/dist/surface/http-server.js +4 -2
  301. package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
  302. package/dist/surface/mcp/mcp-proxy.js +5 -5
  303. package/dist/surface/mcp/mcp.js +4 -2
  304. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
  305. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
  306. package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
  307. package/dist/surface/mcp/server-instructions.js +21 -8
  308. package/dist/surface/tools/act/after-match.d.ts +17 -0
  309. package/dist/surface/tools/act/after-match.js +30 -0
  310. package/dist/surface/tools/act/already-true.d.ts +40 -0
  311. package/dist/surface/tools/act/already-true.js +36 -0
  312. package/dist/surface/tools/act/durable.d.ts +69 -0
  313. package/dist/surface/tools/act/durable.js +139 -0
  314. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +19 -6
  315. package/dist/surface/tools/act/navigation/navigate-arrival.js +60 -10
  316. package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
  317. package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
  318. package/dist/surface/tools/act/proved-controls.d.ts +9 -0
  319. package/dist/surface/tools/act/proved-controls.js +11 -0
  320. package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
  321. package/dist/surface/tools/act/sequence-grade.js +32 -1
  322. package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
  323. package/dist/surface/tools/act/settle-in-flight.js +29 -6
  324. package/dist/surface/tools/act-capsule.d.ts +7 -0
  325. package/dist/surface/tools/act-capsule.js +68 -8
  326. package/dist/surface/tools/act-sequence-tool.js +25 -8
  327. package/dist/surface/tools/act-tools.js +51 -74
  328. package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
  329. package/dist/surface/tools/args/numeric-bounds.js +30 -3
  330. package/dist/surface/tools/assert/assert-grade.js +26 -3
  331. package/dist/surface/tools/assert/assert-verdict.js +32 -6
  332. package/dist/surface/tools/browser-tools.js +17 -4
  333. package/dist/surface/tools/coverage-tools.js +27 -1
  334. package/dist/surface/tools/dynamic-tools.js +6 -0
  335. package/dist/surface/tools/error-recovery.js +4 -4
  336. package/dist/surface/tools/feedback-invite.js +6 -4
  337. package/dist/surface/tools/feedback-tools.d.ts +2 -1
  338. package/dist/surface/tools/feedback-tools.js +1 -1
  339. package/dist/surface/tools/harness-explore.d.ts +3 -2
  340. package/dist/surface/tools/harness-explore.js +7 -6
  341. package/dist/surface/tools/invoke-tool.js +17 -4
  342. package/dist/surface/tools/lease-hint.d.ts +8 -0
  343. package/dist/surface/tools/lease-hint.js +10 -0
  344. package/dist/surface/tools/lease-tools.d.ts +16 -28
  345. package/dist/surface/tools/lease-tools.js +68 -11
  346. package/dist/surface/tools/lineage-tools.js +1 -1
  347. package/dist/surface/tools/live-call-text.js +4 -1
  348. package/dist/surface/tools/observe-tools.js +23 -7
  349. package/dist/surface/tools/real-input-attempt.d.ts +0 -5
  350. package/dist/surface/tools/real-input-attempt.js +1 -1
  351. package/dist/surface/tools/tool-kit.d.ts +5 -0
  352. package/dist/surface/tools/tool-kit.js +6 -1
  353. package/dist/surface/tools/tool-surface.js +17 -5
  354. package/dist/surface/tools/tools.js +68 -29
  355. package/dist/telemetry/argument-shape.d.ts +0 -2
  356. package/dist/telemetry/argument-shape.js +0 -4
  357. package/dist/telemetry/error-class.d.ts +2 -3
  358. package/dist/telemetry/error-class.js +2 -3
  359. package/dist/telemetry/feedback-local.d.ts +1 -0
  360. package/dist/telemetry/feedback-local.js +1 -3
  361. package/dist/telemetry/feedback.d.ts +1 -1
  362. package/dist/telemetry/harness-drive.d.ts +0 -2
  363. package/dist/telemetry/harness-drive.js +0 -4
  364. package/dist/telemetry/hud-metrics.d.ts +32 -0
  365. package/dist/telemetry/hud-metrics.js +95 -0
  366. package/dist/telemetry/session-metrics.d.ts +5 -0
  367. package/dist/telemetry/session-metrics.js +15 -1
  368. package/dist/wire-journal.d.ts +34 -0
  369. package/dist/wire-journal.js +104 -0
  370. package/dist/workspace-packages.d.ts +0 -2
  371. package/dist/workspace-packages.js +1 -9
  372. package/docs/README.md +5 -2
  373. package/docs/actions.mdx +3 -3
  374. package/docs/agent-cheatsheet.md +4 -4
  375. package/docs/architecture.md +6 -6
  376. package/docs/best-practices.mdx +1 -1
  377. package/docs/capabilities.mdx +32 -1
  378. package/docs/cli/cloud.mdx +21 -9
  379. package/docs/cli/doctor.mdx +4 -0
  380. package/docs/cli/feedback.mdx +2 -2
  381. package/docs/cli/gate.mdx +9 -4
  382. package/docs/cli/init.mdx +1 -0
  383. package/docs/cli/report.mdx +44 -0
  384. package/docs/cli/update.mdx +10 -0
  385. package/docs/cli.mdx +9 -0
  386. package/docs/desktop.mdx +14 -10
  387. package/docs/environment.mdx +87 -0
  388. package/docs/faq.mdx +18 -16
  389. package/docs/first-drive.md +5 -13
  390. package/docs/flows.md +32 -2
  391. package/docs/frameworks.mdx +140 -9
  392. package/docs/getting-started.md +3 -3
  393. package/docs/http-transport.md +6 -3
  394. package/docs/human-control.md +2 -2
  395. package/docs/index.mdx +16 -6
  396. package/docs/install-agentic.mdx +37 -1
  397. package/docs/install-manual.mdx +1 -1
  398. package/docs/integration-patterns.md +3 -3
  399. package/docs/multi-agent-testing.md +1 -1
  400. package/docs/packages/babel-plugin.mdx +1 -1
  401. package/docs/packages/browser.mdx +2 -2
  402. package/docs/packages/react.mdx +2 -2
  403. package/docs/packages/tauri.mdx +1 -1
  404. package/docs/packages/vite-plugin.mdx +2 -2
  405. package/docs/packages.mdx +9 -9
  406. package/docs/predicates.mdx +176 -7
  407. package/docs/quickstart.mdx +2 -2
  408. package/docs/recipes.mdx +2 -2
  409. package/docs/state-management.mdx +2 -2
  410. package/docs/system-map.md +1 -1
  411. package/docs/telemetry.md +1 -1
  412. package/docs/token-efficiency.md +3 -3
  413. package/docs/tools/act-and-wait.mdx +1 -0
  414. package/docs/tools/act-sequence.mdx +1 -1
  415. package/docs/tools/console.mdx +1 -1
  416. package/docs/tools/inspect.mdx +1 -1
  417. package/docs/tools/network.mdx +1 -1
  418. package/docs/tools/query.mdx +11 -11
  419. package/docs/tools/sessions.mdx +1 -1
  420. package/docs/tools/snapshot.mdx +1 -1
  421. package/docs/tools/state.mdx +1 -1
  422. package/docs/tools/wait-for.mdx +1 -1
  423. package/docs/usage.md +8 -7
  424. package/docs/use-cases.mdx +157 -0
  425. package/docs/vs-screenshots.mdx +3 -3
  426. package/docs/what-is-recorded.md +62 -0
  427. package/docs/what-is-reticle.mdx +17 -20
  428. package/docs/why-reticle.mdx +14 -21
  429. package/package.json +11 -10
  430. package/dist/judgement/capsule/minimize.d.ts +0 -12
  431. package/dist/judgement/capsule/minimize.js +0 -22
  432. package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
  433. package/dist/judgement/outcome/predicate-to-expect.js +0 -148
  434. package/dist/judgement/phenomena/phenomena.d.ts +0 -26
  435. package/dist/judgement/phenomena/phenomena.js +0 -101
  436. package/dist/language/flows/flow-report.d.ts +0 -22
  437. package/dist/language/flows/flow-report.js +0 -106
@@ -7,7 +7,7 @@ import { z } from 'zod';
7
7
  import { aliasParam } from './args/alias-args.js';
8
8
  import { CONSOLE_ATTACH_NOTE, CONSOLE_LEVELS, ReticleCommand, DEFAULT_ASSERT_TIMEOUT_MS, PredicateKind, Verified, } from '@reticlehq/core';
9
9
  import { ReticleTool } from '@reticlehq/core';
10
- import { countSchema, cursorSchema, httpStatusSchema, timeoutMsSchema, windowMsSchema, } from './args/numeric-bounds.js';
10
+ import { countSchema, cursorSchema, httpStatusSchema, timeoutMsSchema, waitForTimeoutMsSchema, resumeAfter, MAX_BLOCKING_WAIT_MS, windowMsSchema, } from './args/numeric-bounds.js';
11
11
  import { buildReactionReport } from '@reticlehq/engine/question/reaction.js';
12
12
  import { findContradictions } from '@reticlehq/engine/disagreement/contradictions.js';
13
13
  import { evaluatePredicate, waitForPredicate, PredicateSchema, } from '@reticlehq/engine/question/predicate/predicate.js';
@@ -16,7 +16,7 @@ import { WALL_CLOCK } from '../../portal/session/timing/wall-clock.js';
16
16
  import { parsePredicate } from '@reticlehq/engine/question/predicate/predicate-parse.js';
17
17
  import { matchNet, matchConsole, isConsoleEvent, eventMatchesFilters, netEmptyHint, consoleEmptyHint, reconcileNet, projectNetCall, projectConsoleLog, withoutUrlRaw, } from '@reticlehq/engine/window/event-filters.js';
18
18
  import { applyEventBudget, costHint, withSizeCost, DEFAULT_OBSERVE_EVENT_LIMIT, DEFAULT_QUERY_LIMIT, } from '../../portal/session/output-budget.js';
19
- import { annotateStarvedFailure, healthEnvelope, bufferEnvelope, } from '../../portal/session/session-health.js';
19
+ import { annotateStarvedFailure, currentOf, healthEnvelope, bufferEnvelope, } from '../../portal/session/session-health.js';
20
20
  import { assertsDerivedIpcStatus, DERIVED_IPC_STATUS_ADVICE, isPresenceOnlyAssertion, PRESENCE_ONLY_ADVICE, } from './assert/assert-grade.js';
21
21
  import { assertVerdict } from './assert/assert-verdict.js';
22
22
  import { followLostObservation } from './act/act-observation.js';
@@ -33,6 +33,7 @@ import { withControl } from '../../portal/session/control-envelope.js';
33
33
  import { asNumber, asRecord, asString } from '@reticlehq/core';
34
34
  import { intentArg, sessionIdShape, commandOrThrow } from './tool-kit.js';
35
35
  import { gradeOfPredicate } from './assert/assert-grade.js';
36
+ import { captureAssertion } from '../../language/flows/replay.js';
36
37
  /**
37
38
  * Evidence-completeness block: present on observe/network/console only when the ring buffer has
38
39
  * evicted events, so a "no such event" answer is distinguishable from "I dropped it" (issue #27).
@@ -172,6 +173,7 @@ export const OBSERVE_TOOLS = [
172
173
  currentDocumentId: session.currentDocumentId,
173
174
  currentEditEpoch: session.currentEditEpoch,
174
175
  appOrigin: session.url,
176
+ background: session.background,
175
177
  ...(judgingTheAct ? { ...session.lastAct.effect(), actionSince: actCursor } : {}),
176
178
  });
177
179
  // carry session health — a throttled tab means the observed timeline may be incomplete.
@@ -193,15 +195,19 @@ export const OBSERVE_TOOLS = [
193
195
  predicate: PredicateSchema.optional().describe('Predicate to wait for: { signal }, { net }, { element }, { kind: "net", ok: false } (assert on the OUTCOME — the honest field for IPC, which has no status code), { kind: "state", store, path, equals } (assert a registered store\'s value directly — the source of truth no DOM read can reach; equals takes a literal or { $gte | $contains | $length } pattern), { kind: "settled", quietMs } (deterministic network + DOM idle — prefer this over a fixed sleep), or a combination via allOf/anyOf.'),
194
196
  // Same concept, the neighbouring tool's name. See alias-args.ts.
195
197
  until: PredicateSchema.optional().describe("Alias for `predicate` (act_and_wait's name)."),
196
- timeout_ms: timeoutMsSchema
198
+ timeout_ms: waitForTimeoutMsSchema
197
199
  .optional()
198
- .describe('Maximum wait in milliseconds. Default: 4000. Capped at 55000: your MCP client aborts the request before a longer wait can return, so a bound above this would be advertised and not deliverable. To outlast it, poll — several short waits, each of which returns a verdict.'),
200
+ .describe(`Maximum wait in milliseconds. Default: 4000, up to 600000. One call waits at most ${String(MAX_BLOCKING_WAIT_MS)} ms, because your MCP client aborts a longer request; past that it returns resume_ms, and you call again with timeout_ms: resume_ms and the same since to keep waiting.`),
199
201
  since: cursorSchema
200
202
  .optional()
201
203
  .describe('Cursor from a prior reticle_act — scopes the wait to events after that act.'),
202
204
  ...sessionIdShape,
203
205
  },
204
206
  outputSchema: {
207
+ resume_ms: z
208
+ .number()
209
+ .optional()
210
+ .describe('Present when this call stopped at the per-call limit before the predicate was seen. Call again with timeout_ms set to this, the same predicate and the same since, to keep waiting.'),
205
211
  pass: z.boolean(),
206
212
  evidence: z.unknown().optional(),
207
213
  failureReason: z.string().optional(),
@@ -239,7 +245,9 @@ export const OBSERVE_TOOLS = [
239
245
  .describe('Where the code behind this verdict lives, as `file:line`. For an element/text assertion it is the matched element itself; for a FAILING signal/net/state assertion — which has no element to point at — it is the control last acted on, where the handler that should have fired lives. OMITTED when neither is known: this tool never borrows an unrelated location.'),
240
246
  },
241
247
  handler: async (deps, args) => {
242
- const waitBudget = asNumber(args['timeout_ms']) ?? DEFAULT_ASSERT_TIMEOUT_MS;
248
+ const requestedMs = asNumber(args['timeout_ms']) ?? DEFAULT_ASSERT_TIMEOUT_MS;
249
+ // One call never blocks past what the client allows; the rest comes back as resume_ms.
250
+ const waitBudget = Math.min(requestedMs, MAX_BLOCKING_WAIT_MS);
243
251
  // Spend the budget waiting for the APP as well as for the predicate. See resolve-within.
244
252
  let session = await resolveSessionWithin(deps.sessions, asString(args['sessionId']), waitBudget, WALL_CLOCK);
245
253
  // `until` is act_and_wait's name for this — see alias-args.ts.
@@ -275,6 +283,7 @@ export const OBSERVE_TOOLS = [
275
283
  ...annotateStarvedFailure(session, verdict),
276
284
  ...assertionSource(session, predicate, verdict),
277
285
  ...(followed.followed ? { sessionId: session.id } : {}),
286
+ ...((resume) => (resume === undefined ? {} : { resume_ms: resume }))(resumeAfter(requestedMs, waitBudget, verdict)),
278
287
  ...healthEnvelope(session),
279
288
  ...bufferEnvelope(session),
280
289
  });
@@ -294,7 +303,7 @@ export const OBSERVE_TOOLS = [
294
303
  '{ net, urlContains|method|status|count|bodyMatches|bodyContains|requestBodyContains|requestBodyMatches } ' +
295
304
  '{ state, path|equals } { route, pathname (exact) | contains (path+query+hash) } ' +
296
305
  '{ element, testid|role|text } { text } { console, level|contains|absent } { animation, name } ' +
297
- '{ settled } — combine with { allOf | anyOf | not }. Prefer a signal/net/state consequence ' +
306
+ '{ settled } { compare, left, right } — combine with { allOf | anyOf | not }. Prefer a signal/net/state consequence ' +
298
307
  'over element/text presence.'),
299
308
  until: PredicateSchema.optional().describe("Alias for `predicate` (act_and_wait's name)."),
300
309
  timeout_ms: timeoutMsSchema
@@ -438,6 +447,13 @@ export const OBSERVE_TOOLS = [
438
447
  // destroys — see runs/run-context.ts. Recorded WITHOUT an attribution window: this tool drives
439
448
  // nothing, so no event it observed was caused by it.
440
449
  session.recordAction(ReticleTool.ASSERT, asRecord(args), verdictEffect);
450
+ // A passing check over the default window (since the last act) is proof of that act, so it
451
+ // joins that step in the recording — otherwise "act, then assert" saves a bare click. An
452
+ // explicit `since` may reach back past the last step, where replay would not find it. The
453
+ // store itself refuses when a navigation came after that step: see RecordingStore.markNavigated.
454
+ if (Verified.YES === decision['verified'] && args['since'] === undefined) {
455
+ captureAssertion(deps.recordings, predicate);
456
+ }
441
457
  return withControl(session, {
442
458
  ...decision,
443
459
  ...annotateStarvedFailure(session, verdict),
@@ -454,7 +470,7 @@ export const OBSERVE_TOOLS = [
454
470
  ...coverage,
455
471
  // The SAME pointer the journal keeps, not a second lookup — one verdict, one file:line.
456
472
  ...(verdictEffect.source === undefined ? {} : { source: verdictEffect.source }),
457
- ...healthEnvelope(session),
473
+ ...healthEnvelope(currentOf(deps.sessions, session)),
458
474
  ...bufferEnvelope(session),
459
475
  });
460
476
  },
@@ -22,11 +22,6 @@
22
22
  import { ActionType, InputModeReason } from '@reticlehq/core';
23
23
  import type { Session } from '../../portal/session/session.js';
24
24
  import { type ToolDeps } from './tool-kit.js';
25
- /**
26
- * Expand a leading `~` against the caller's home directory. Absolute and relative paths pass
27
- * through unchanged. `home` is injected so unit tests do not depend on the machine's real home.
28
- */
29
- export declare function expandUserPath(rawPath: string, home: string): string;
30
25
  /**
31
26
  * If the action is `upload` AND the inner args carry `path`, read the file from disk and rewrite
32
27
  * the args to `{ content, name, type, __base64: true }` before the ACT command reaches the browser.
@@ -99,7 +99,7 @@ const UPLOAD_MAX_BYTES = Math.floor((TRANSPORT_LIMITS.MAX_MESSAGE_BYTES / (4 / 3
99
99
  * Expand a leading `~` against the caller's home directory. Absolute and relative paths pass
100
100
  * through unchanged. `home` is injected so unit tests do not depend on the machine's real home.
101
101
  */
102
- export function expandUserPath(rawPath, home) {
102
+ function expandUserPath(rawPath, home) {
103
103
  if ('~' === rawPath)
104
104
  return home;
105
105
  if (rawPath.startsWith('~/') || rawPath.startsWith('~\\'))
@@ -222,6 +222,11 @@ export declare const EnvelopeKey: {
222
222
  readonly VERIFY_NEXT: "verify_next";
223
223
  /** The contextual "tell us what just went wrong" invitation. Same story, same release. */
224
224
  readonly FEEDBACK_INVITE: "feedback_invite";
225
+ /**
226
+ * The same moment, for the HUMAN: a call with the founder, or an email through `identify`. The
227
+ * agent is told to offer it and never to book or submit anything itself.
228
+ */
229
+ readonly TALK_TO_US: "talk_to_us";
225
230
  /** SDK/daemon version skew — often the one fact that explains everything else in the session. */
226
231
  readonly VERSION_SKEW: "version_skew";
227
232
  /** A feedback report that was accepted and then failed to send. Only the reporter can act on it. */
@@ -28,7 +28,7 @@ export const intentArg = z
28
28
  'BAD: "P1 step 5", "drive 2 of 2", "same as before the fix", "the board renders cleanly" — these name a session, ' +
29
29
  'a step number or nothing checkable, and the person reading them later has none of that context. ' +
30
30
  'This is shared memory: it is pooled per project and later agents read it back to avoid re-deriving what you just established. ' +
31
- 'Recorded in .reticle/intent.json, the same ledger reticle_intent writes, and marked proved by this verdict if it passes.');
31
+ 'Recorded in .reticle/intent/, the same ledger reticle_intent writes, and marked proved by this verdict if it passes.');
32
32
  export const sessionIdShape = {
33
33
  sessionId: z
34
34
  .string()
@@ -75,6 +75,11 @@ export const EnvelopeKey = {
75
75
  VERIFY_NEXT: 'verify_next',
76
76
  /** The contextual "tell us what just went wrong" invitation. Same story, same release. */
77
77
  FEEDBACK_INVITE: 'feedback_invite',
78
+ /**
79
+ * The same moment, for the HUMAN: a call with the founder, or an email through `identify`. The
80
+ * agent is told to offer it and never to book or submit anything itself.
81
+ */
82
+ TALK_TO_US: 'talk_to_us',
78
83
  /** SDK/daemon version skew — often the one fact that explains everything else in the session. */
79
84
  VERSION_SKEW: 'version_skew',
80
85
  /** A feedback report that was accepted and then failed to send. Only the reporter can act on it. */
@@ -395,11 +395,23 @@ export function describeToolSurface(active, requested) {
395
395
  export function defaultAdvertisedNames() {
396
396
  const surface = resolveToolSurface();
397
397
  const names = [...filterToolNames(surface)];
398
- // `reticle_run` is real only where the surface keeps a dispatch hatch. The merged surface ships
399
- // the catalogue without one, and naming a hatch that is not there is the same defect one level in.
400
- return TOOL_SURFACE.MERGED === surface
401
- ? [...names, ReticleTool.TOOLS]
402
- : [...names, ReticleTool.TOOLS, ReticleTool.RUN];
398
+ /*
399
+ * Both meta tools, on every surface, because that is what the MCP actually advertises.
400
+ *
401
+ * This used to withhold `reticle_run` from the merged surface, reasoning that "naming a hatch
402
+ * that is not there is the same defect one level in". The reasoning is sound and the premise was
403
+ * false: `buildDynamicTools` returns `[reticle_tools, reticle_run]` unconditionally and
404
+ * `advertisedTools` appends both to every surface, so the hatch has always been there. Measured
405
+ * against a live daemon on the default surface: `reticle_run` is advertised, and
406
+ * `reticle_run { tool: "reticle_lease" }` reaches `reticle_lease`.
407
+ *
408
+ * Two functions answering one question, and this was the one the MCP never called. It is read by
409
+ * `server-instructions`, which gates "Everything else is one hop: reticle_tools lists it,
410
+ * reticle_run calls it" on this list - so agents were told the hatch was absent from a surface
411
+ * carrying it, and six recovery messages were later rewritten to match the lie.
412
+ * `advertised-names-are-real.test.ts` now pins this to `advertisedTools` itself.
413
+ */
414
+ return [...names, ReticleTool.TOOLS, ReticleTool.RUN];
403
415
  }
404
416
  /** The advertised NAME set for a surface, without needing the tool table. */
405
417
  function filterToolNames(surface) {
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { NoSessionAction, QueryBy, ReticleCommand, SnapshotMode } from '@reticlehq/core';
2
+ import { DiscoveryInvite, AttrNamesSchema, EventType, NoSessionAction, QueryBy, ReticleCommand, SnapshotMode, } from '@reticlehq/core';
3
3
  import { ReticleTool } from '@reticlehq/core';
4
4
  import { withSizeCost } from '../../portal/session/output-budget.js';
5
5
  import { applySnapshotDelta, SnapshotCache } from './read/snapshot-delta.js';
@@ -109,7 +109,7 @@ export const RAW_TOOLS = [
109
109
  lastOutage: z.object({ startedMs: z.number(), durationMs: z.number() }).optional(),
110
110
  })
111
111
  .optional()
112
- .describe('Present only when this tab has dropped and reconnected at least once: how long it has been attached in total, how many outages occurred, and when the last one started and how long it lasted. A verdict over this window spans a gap where nothing was observed.'),
112
+ .describe('Continuity of the SDK-to-daemon LINK for this tab, present for any tab the daemon has a record of: how long this attachment has lasted, how many times it dropped and came back, and when the last gap started and how long it lasted. `outages > 0` means a verdict over this window spans a gap where nothing was observed. These are link counters, NOT app health: they say nothing about whether the page origin is still serving, so a tab whose dev server has died reports `outages: 0` with a growing `connectedSinceMs` and is not evidence the app is up.'),
113
113
  }))
114
114
  .describe('Connected browser sessions with health state. `projectId` groups sessions by app (stable across port changes); `leased` marks a pool-managed headless context vs a human tab.'),
115
115
  why: z
@@ -125,6 +125,10 @@ export const RAW_TOOLS = [
125
125
  })
126
126
  .optional()
127
127
  .describe("Present ONLY when `sessions` is empty: the same answer as `why`, executable. `command` is the LITERAL command to run, sourced from this project's own package.json scripts and lockfile — it is absent, never guessed, when the project declares no dev script. `action` is one of daemon_split | start_dev_server | run_init | open_app | reopen_app. `daemon_split` outranks the rest and means the app IS running and instrumented, on a DIFFERENT daemon than the one you are attached to — do not start or re-init anything, read `reason`."),
128
+ talk_to_us: z
129
+ .string()
130
+ .optional()
131
+ .describe('Present ONLY when `sessions` is empty: a call with the founder for the person you are working with. Offer it; never act on it.'),
128
132
  lastKnown: z
129
133
  .object({
130
134
  sessionId: z.string(),
@@ -158,6 +162,8 @@ export const RAW_TOOLS = [
158
162
  ...(why === undefined ? {} : { why }),
159
163
  ...(next === undefined ? {} : { next_action: next }),
160
164
  ...(undefined === lastKnown ? {} : { lastKnown }),
165
+ // Setup that never connects is where most people give up; it is the talk worth having.
166
+ talk_to_us: DiscoveryInvite.AGENT,
161
167
  };
162
168
  }
163
169
  return { sessions };
@@ -230,6 +236,10 @@ export const RAW_TOOLS = [
230
236
  .boolean()
231
237
  .optional()
232
238
  .describe('True when the page exceeded the snapshot cap. The tree is then a document-order PREFIX: an element being absent from it does NOT mean it is absent from the page.'),
239
+ // The refs a capped read never entered — re-read one with `scope`. Undeclared, a validating
240
+ // client stripped it, and a truncated tree arrived with no way to finish reading it.
241
+ unread: z.array(z.string()).optional(),
242
+ unreadOverflow: z.boolean().optional(),
233
243
  note: z
234
244
  .string()
235
245
  .optional()
@@ -261,7 +271,7 @@ export const RAW_TOOLS = [
261
271
  scope: asString(args['scope']) ?? '',
262
272
  mode,
263
273
  diff: true === args['diff'],
264
- }, deps.snapshots ?? SNAPSHOT_CACHE), mode), mode), mode)));
274
+ }, deps.snapshots ?? SNAPSHOT_CACHE), mode), mode), mode, resolved)));
265
275
  },
266
276
  },
267
277
  {
@@ -290,10 +300,7 @@ export const RAW_TOOLS = [
290
300
  .boolean()
291
301
  .optional()
292
302
  .describe('Return the `scope` element ITSELF instead of searching inside it. Use when the target is a plain layout container with no role, name, testid or text of its own — which is routinely the element that carries the handler, and is otherwise unreachable because every query excludes its own scope root. Requires `scope`.'),
293
- attrs: z
294
- .array(z.string())
295
- .optional()
296
- .describe("Attribute names to return per match, e.g. ['href'] to inventory links or ['src'] for images. Absent attributes are omitted; credential-bearing names are redacted."),
303
+ attrs: AttrNamesSchema.optional().describe("Attribute NAMES to return per match, e.g. ['href'] for links, ['src'] for images. Projects, never filters: 'name=value' is refused. Absent omitted; credentials redacted."),
297
304
  limit: countSchema
298
305
  .optional()
299
306
  .describe('Cap the returned descriptors to the first N (cuts tokens on broad queries). If more matched, the result carries total + truncated:true so the trim is never silent — narrow with name/scope.'),
@@ -371,6 +378,12 @@ export const RAW_TOOLS = [
371
378
  .optional()
372
379
  .describe('Structural clusters on the page — what IS here, to diagnose the miss.'),
373
380
  presentTestids: z.array(z.string()).optional(),
381
+ // Declared for the same reason presentRegions had to be: undeclared here, it is stripped
382
+ // from structuredContent without a word — and this field exists precisely to say a word.
383
+ presentTestidsTotal: z
384
+ .number()
385
+ .optional()
386
+ .describe('Present only when presentTestids was cut at its cap: how many there were. The list is document order, so a region low on the page is what got dropped — absence from the list proves nothing.'),
374
387
  knownEmptyState: z.boolean(),
375
388
  splitText: z
376
389
  .object({
@@ -614,24 +627,31 @@ function noteEmptyLeanTree(result, mode) {
614
627
  * wrapper or two is ordinary, so this is deliberately not `=== 0`.
615
628
  */
616
629
  const UNMOUNTED_ROOT_MAX_ELEMENTS = 3;
617
- /**
618
- * The third cause of an empty tree, and the one the walk cannot see: the app is not mounted.
619
- *
620
- * Reported from the field with the react-three-fiber crash. The source-mapping stamp threw inside
621
- * R3F's commit phase, React unmounted the entire tree, and the page went white — and the snapshot
622
- * answered `{ tree: "", nodes: 0 }`, which is also what a page that has not rendered yet answers.
623
- * The reporter spent a diagnosis pass separating the two, and said that detecting a dead root would
624
- * have pointed straight at the cause.
625
- *
626
- * The other two notes here explain an empty tree by what the WALK passed over. This one cannot: a
627
- * walk that visited nothing has nothing to have skipped. It reads the DOM count instead, which is
628
- * why the browser now reports it, and it fires only when the skip counts are silent — a page whose
629
- * elements were all hidden has plenty of DOM and belongs to `noteHiddenPage`.
630
- *
631
- * Like both of its neighbours it names a fact and hands over the next read, and does not diagnose:
632
- * "not mounted" is certain from the count, WHY is not, and the console is where the answer is.
633
- */
634
- function noteUnmountedRoot(result, mode) {
630
+ function crashEvidence(session) {
631
+ const since = asRecord(session)['eventsSince'];
632
+ if ('function' !== typeof since)
633
+ return undefined;
634
+ let events;
635
+ try {
636
+ events = since.call(session, 0);
637
+ }
638
+ catch {
639
+ return undefined;
640
+ }
641
+ if (!Array.isArray(events))
642
+ return undefined;
643
+ const errors = events.filter((e) => {
644
+ const type = asString(asRecord(e)['type']);
645
+ return EventType.CONSOLE_ERROR === type || EventType.ERROR_UNCAUGHT === type;
646
+ });
647
+ if (0 === errors.length)
648
+ return undefined;
649
+ const first = errors
650
+ .map((e) => asString(asRecord(asRecord(e)['data'])['message']))
651
+ .find((m) => undefined !== m && '' !== m);
652
+ return { count: errors.length, first: first ?? '' };
653
+ }
654
+ function noteUnmountedRoot(result, mode, session) {
635
655
  if (SnapshotMode.STATUS === mode)
636
656
  return result;
637
657
  const row = asRecord(result);
@@ -642,12 +662,31 @@ function noteUnmountedRoot(result, mode) {
642
662
  const elements = asNumber(row['domElements']);
643
663
  if (elements === undefined || elements > UNMOUNTED_ROOT_MAX_ELEMENTS)
644
664
  return result;
665
+ const mount = `the tree is empty because there is almost nothing in the DOM: ${String(elements)} ` +
666
+ `element(s) under this scope. That is a mount container with nothing rendered into it, not a ` +
667
+ `page whose contents were skipped — so the app is UNMOUNTED rather than slow, and waiting ` +
668
+ `will not change it. `;
669
+ // An uncaught error beside an empty mount container is the difference between "has not started"
670
+ // and "started and died", which are the two readings of this count and want opposite next moves.
671
+ // Nothing here is framework-specific, and deliberately so: the condition is a known mount
672
+ // container that holds nothing with an uncaught error beside it, which is as true of a Vue or
673
+ // Svelte root as a React one.
674
+ const crash = crashEvidence(session);
675
+ if (crash !== undefined) {
676
+ return {
677
+ ...row,
678
+ note: mount +
679
+ `The app did not merely fail to start — it CRASHED: ${String(crash.count)} uncaught ` +
680
+ `error(s) were logged in this session` +
681
+ ('' === crash.first ? '' : `, the first: "${crash.first}"`) +
682
+ `. Read reticle_console for the rest, and check anything that runs inside a framework ` +
683
+ `commit phase. Loading the app again will reproduce it rather than fix it.`,
684
+ };
685
+ }
645
686
  return {
646
687
  ...row,
647
- note: `the tree is empty because there is almost nothing in the DOM: ${String(elements)} ` +
648
- `element(s) under this scope. That is a mount container with nothing rendered into it, not a ` +
649
- `page whose contents were skipped — so the app is UNMOUNTED rather than slow, and waiting ` +
650
- `will not change it. An app that was rendering and then stopped has usually thrown: read ` +
688
+ note: mount +
689
+ `An app that was rendering and then stopped has usually thrown: read ` +
651
690
  `reticle_console for an uncaught error, and check anything that runs inside a framework ` +
652
691
  `commit phase. If the app has genuinely not started yet, load it and snapshot again.`,
653
692
  };
@@ -19,8 +19,6 @@
19
19
  * never quietly start forwarding free text.
20
20
  */
21
21
  export declare const OTHER_VALUE = "other";
22
- /** True for anything we are willing to report a value for. */
23
- export declare function isSafeEnumParam(name: string): boolean;
24
22
  /**
25
23
  * The reportable form of one parameter: `name` on its own, or `name:value` when the parameter is a
26
24
  * known enum. Never the raw value of anything else.
@@ -41,10 +41,6 @@ const SAFE_ENUM_PARAMS = new Map([
41
41
  ['level', new Set(['error', 'warn', 'info', 'log', 'debug'])],
42
42
  ]);
43
43
  export const OTHER_VALUE = 'other';
44
- /** True for anything we are willing to report a value for. */
45
- export function isSafeEnumParam(name) {
46
- return SAFE_ENUM_PARAMS.has(name);
47
- }
48
44
  /**
49
45
  * The reportable form of one parameter: `name` on its own, or `name:value` when the parameter is a
50
46
  * known enum. Never the raw value of anything else.
@@ -2,9 +2,8 @@
2
2
  * Whose defect was that tool error?
3
3
  *
4
4
  * `toolErrors` was one number covering three failures with three different fixes, so the top-line
5
- * count could be acted on only by unpacking `errors[]` by hand and reading the prose. Measured over
6
- * 2026-08-10/11: 60 `state`, 22 `refusal`, 20 `schema` (serialized zod arrays) and 20 stale refs.
7
- * Read as "126 tool errors" that hides the sixth of them where OUR schema failed to explain itself —
5
+ * count could be acted on only by unpacking `errors[]` by hand and reading the prose. Read as one
6
+ * number, it hides the share where OUR schema failed to explain itself (serialized zod arrays),
8
7
  * which is the only bucket we can fix by writing better descriptions.
9
8
  *
10
9
  * Classified from the message SHAPE, never stored raw: this returns a bucket, and the caller keeps
@@ -2,9 +2,8 @@
2
2
  * Whose defect was that tool error?
3
3
  *
4
4
  * `toolErrors` was one number covering three failures with three different fixes, so the top-line
5
- * count could be acted on only by unpacking `errors[]` by hand and reading the prose. Measured over
6
- * 2026-08-10/11: 60 `state`, 22 `refusal`, 20 `schema` (serialized zod arrays) and 20 stale refs.
7
- * Read as "126 tool errors" that hides the sixth of them where OUR schema failed to explain itself —
5
+ * count could be acted on only by unpacking `errors[]` by hand and reading the prose. Read as one
6
+ * number, it hides the share where OUR schema failed to explain itself (serialized zod arrays),
8
7
  * which is the only bucket we can fix by writing better descriptions.
9
8
  *
10
9
  * Classified from the message SHAPE, never stored raw: this returns a bucket, and the caller keeps
@@ -1,4 +1,5 @@
1
1
  import type { Feedback } from '@reticlehq/core/telemetry';
2
+ /** Reports live beside the rest of the workspace, not in a temp dir somebody has to be told about. */
2
3
  interface SavedFeedback {
3
4
  path: string;
4
5
  /** A ready-to-run command that turns the file into an issue. */
@@ -13,8 +13,6 @@
13
13
  import { mkdirSync, writeFileSync } from 'node:fs';
14
14
  import { join } from 'node:path';
15
15
  import { ReticleDir } from '@reticlehq/core';
16
- /** Reports live beside the rest of the workspace, not in a temp dir somebody has to be told about. */
17
- const FEEDBACK_SUBDIR = 'feedback';
18
16
  /** Filesystem-safe, sortable, and unique enough for one report per second per kind. */
19
17
  const stamp = (now) => new Date(now).toISOString().replace(/[:.]/g, '-').replace('Z', '');
20
18
  const asMarkdown = (input, text, context) => {
@@ -39,7 +37,7 @@ const asMarkdown = (input, text, context) => {
39
37
  */
40
38
  export function saveFeedbackLocally(cwd, input, text, context, now = Date.now()) {
41
39
  try {
42
- const dir = join(cwd, ReticleDir.ROOT, FEEDBACK_SUBDIR);
40
+ const dir = join(cwd, ReticleDir.ROOT, ReticleDir.FEEDBACK_SUBDIR);
43
41
  mkdirSync(dir, { recursive: true });
44
42
  const path = join(dir, `${stamp(now)}-${input.kind ?? 'note'}.md`);
45
43
  writeFileSync(path, asMarkdown(input, text, context), 'utf8');
@@ -27,7 +27,7 @@ import { type SessionFacts } from './feedback-context.js';
27
27
  export declare const FEEDBACK_ENV = "RETICLE_FEEDBACK";
28
28
  /** What replaces a redacted match. Visible on purpose: a reader must see that something was removed. */
29
29
  export declare const REDACTED = "[redacted]";
30
- export interface Redaction {
30
+ interface Redaction {
31
31
  text: string;
32
32
  /** Which rules fired, deduped. Surfaced back to the author so a redaction is never silent. */
33
33
  removed: string[];
@@ -2,5 +2,3 @@
2
2
  export declare function withHarnessDrive<T>(drive: () => Promise<T>): Promise<T>;
3
3
  /** True while a harness drive is in progress. */
4
4
  export declare function harnessDriving(): boolean;
5
- /** Tests only — a leaked span would mis-attribute every verdict after it. */
6
- export declare function resetHarnessDrive(): void;
@@ -33,7 +33,3 @@ export async function withHarnessDrive(drive) {
33
33
  export function harnessDriving() {
34
34
  return depth > 0;
35
35
  }
36
- /** Tests only — a leaked span would mis-attribute every verdict after it. */
37
- export function resetHarnessDrive() {
38
- depth = 0;
39
- }
@@ -0,0 +1,32 @@
1
+ import { HudPanel, HudView, type HudUseData } from '@reticlehq/core';
2
+ /**
3
+ * How people use the in-page HUD, rolled into the session summary.
4
+ *
5
+ * Windowed like the tool histograms beside it: a periodic flush reports what happened since the
6
+ * last one, so a dashboard sums across a session's events. Names only — every key is a control id
7
+ * the wire schema already checked against core's closed list.
8
+ */
9
+ /** How many steps of the journey one window keeps. A journey longer than this is cut, and says so. */
10
+ export declare const HUD_JOURNEY_MAX = 60;
11
+ export interface HudSummary {
12
+ /** Presses per control; a toggle counts under `id:on` / `id:off` so a switch reads both ways. */
13
+ hudControls?: Record<string, number>;
14
+ /** Milliseconds the HUD spent in each view during the window. */
15
+ hudViewMs?: Partial<Record<HudView, number>>;
16
+ /** Milliseconds each panel was showing during the window. */
17
+ hudPanelMs?: Partial<Record<HudPanel, number>>;
18
+ /** The window's presses and view changes, in order, cut at HUD_JOURNEY_MAX. */
19
+ hudJourney?: string[];
20
+ /** Present, and true, only when the journey was cut. */
21
+ hudJourneyCut?: true;
22
+ }
23
+ export declare class HudMetrics {
24
+ #private;
25
+ constructor(now: () => number);
26
+ record(use: HudUseData): void;
27
+ get empty(): boolean;
28
+ /** The window so far. Open view and panel are counted up to now, and keep running after. */
29
+ summarize(): HudSummary;
30
+ /** Start the next window. The current view and panel carry over, timed from now. */
31
+ reset(): void;
32
+ }
@@ -0,0 +1,95 @@
1
+ import { isHudControl } from '@reticlehq/core/hud';
2
+ import { HudPanel, HudView } from '@reticlehq/core';
3
+ /**
4
+ * How people use the in-page HUD, rolled into the session summary.
5
+ *
6
+ * Windowed like the tool histograms beside it: a periodic flush reports what happened since the
7
+ * last one, so a dashboard sums across a session's events. Names only — every key is a control id
8
+ * the wire schema already checked against core's closed list.
9
+ */
10
+ /** How many steps of the journey one window keeps. A journey longer than this is cut, and says so. */
11
+ export const HUD_JOURNEY_MAX = 60;
12
+ export class HudMetrics {
13
+ #now;
14
+ #controls = new Map();
15
+ #viewMs = new Map();
16
+ #panelMs = new Map();
17
+ #journey = [];
18
+ #cut = false;
19
+ /** Did anything happen this window. A HUD merely sitting open is not worth a flush of its own. */
20
+ #changed = false;
21
+ #view;
22
+ #panel;
23
+ constructor(now) {
24
+ this.#now = now;
25
+ }
26
+ record(use) {
27
+ // A control the HUD never rendered is dropped here, the one place HUD use is counted, so free
28
+ // text cannot ride in on the id.
29
+ const control = use.control !== undefined && isHudControl(use.control) ? use.control : undefined;
30
+ if (control === undefined && use.view === undefined && use.panel === undefined)
31
+ return;
32
+ this.#changed = true;
33
+ if (control !== undefined) {
34
+ const key = use.toggle === undefined ? control : `${control}:${use.toggle}`;
35
+ this.#controls.set(key, (this.#controls.get(key) ?? 0) + 1);
36
+ this.#step(key);
37
+ }
38
+ if (use.view !== undefined && use.view !== this.#view?.value) {
39
+ this.#close(this.#view, this.#viewMs);
40
+ this.#view = { value: use.view, since: this.#now() };
41
+ this.#step(`view:${use.view}`);
42
+ }
43
+ if (use.panel !== undefined && use.panel !== this.#panel?.value) {
44
+ this.#close(this.#panel, this.#panelMs);
45
+ this.#panel = { value: use.panel, since: this.#now() };
46
+ if (use.panel !== HudPanel.NONE)
47
+ this.#step(`panel:${use.panel}`);
48
+ }
49
+ }
50
+ get empty() {
51
+ return !this.#changed;
52
+ }
53
+ /** The window so far. Open view and panel are counted up to now, and keep running after. */
54
+ summarize() {
55
+ const viewMs = this.#withOpen(this.#view, this.#viewMs);
56
+ const panelMs = this.#withOpen(this.#panel, this.#panelMs);
57
+ return {
58
+ ...(this.#controls.size > 0 ? { hudControls: Object.fromEntries(this.#controls) } : {}),
59
+ ...(viewMs.size > 0 ? { hudViewMs: Object.fromEntries(viewMs) } : {}),
60
+ ...(panelMs.size > 0 ? { hudPanelMs: Object.fromEntries(panelMs) } : {}),
61
+ ...(this.#journey.length > 0 ? { hudJourney: [...this.#journey] } : {}),
62
+ ...(this.#cut ? { hudJourneyCut: true } : {}),
63
+ };
64
+ }
65
+ /** Start the next window. The current view and panel carry over, timed from now. */
66
+ reset() {
67
+ const at = this.#now();
68
+ this.#controls.clear();
69
+ this.#viewMs.clear();
70
+ this.#panelMs.clear();
71
+ this.#journey.length = 0;
72
+ this.#cut = false;
73
+ this.#changed = false;
74
+ if (this.#view !== undefined)
75
+ this.#view = { ...this.#view, since: at };
76
+ if (this.#panel !== undefined)
77
+ this.#panel = { ...this.#panel, since: at };
78
+ }
79
+ #step(entry) {
80
+ if (this.#journey.length < HUD_JOURNEY_MAX)
81
+ this.#journey.push(entry);
82
+ else
83
+ this.#cut = true;
84
+ }
85
+ #close(open, into) {
86
+ if (open === undefined)
87
+ return;
88
+ into.set(open.value, (into.get(open.value) ?? 0) + Math.max(0, this.#now() - open.since));
89
+ }
90
+ #withOpen(open, closed) {
91
+ const out = new Map(closed);
92
+ this.#close(open, out);
93
+ return out;
94
+ }
95
+ }
@@ -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;