@reticlehq/server 2.4.0 → 2.6.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 (472) hide show
  1. package/SKILL.md +719 -0
  2. package/dist/bridge/auth-failure-reason.d.ts +19 -0
  3. package/dist/bridge/auth-failure-reason.js +29 -0
  4. package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
  5. package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
  6. package/dist/{bridge.d.ts → bridge/bridge.d.ts} +13 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +95 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
  10. package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
  11. package/dist/capsule/capsule-store.d.ts +8 -3
  12. package/dist/capsule/capsule-store.js +7 -2
  13. package/dist/capsule/capsule.js +3 -3
  14. package/dist/capsule/causal-summary.d.ts +8 -0
  15. package/dist/capsule/causal-summary.js +22 -12
  16. package/dist/capsule/divergence.d.ts +9 -4
  17. package/dist/capsule/divergence.js +8 -8
  18. package/dist/capsule/predicate-to-links.js +11 -8
  19. package/dist/cli/cli-doctor.d.ts +1 -0
  20. package/dist/cli/cli-doctor.js +135 -0
  21. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  22. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  23. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  24. package/dist/cli/cli-launch.js +171 -0
  25. package/dist/cli/cli-parse-feedback.d.ts +31 -0
  26. package/dist/cli/cli-parse-feedback.js +56 -0
  27. package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
  28. package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
  29. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
  30. package/dist/{cli-port.js → cli/cli-port.js} +9 -9
  31. package/dist/cli/cli-update-commands.d.ts +4 -0
  32. package/dist/cli/cli-update-commands.js +93 -0
  33. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  34. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  35. package/dist/cli/doctor-daemon-line.d.ts +37 -0
  36. package/dist/cli/doctor-daemon-line.js +35 -0
  37. package/dist/cli/port-holder.d.ts +43 -0
  38. package/dist/cli/port-holder.js +99 -0
  39. package/dist/cli.d.ts +2 -2
  40. package/dist/cli.js +302 -100
  41. package/dist/cloud/cloud-config.d.ts +0 -1
  42. package/dist/cloud/cloud-config.js +9 -9
  43. package/dist/cloud/cloud-sync.d.ts +1 -7
  44. package/dist/cloud/cloud-sync.js +12 -12
  45. package/dist/crawl/crawl-empty.d.ts +16 -0
  46. package/dist/crawl/crawl-empty.js +24 -0
  47. package/dist/crawl/crawl-tools.js +4 -1
  48. package/dist/crawl/crawl.js +26 -7
  49. package/dist/daemon/daemon-resilience.d.ts +73 -0
  50. package/dist/daemon/daemon-resilience.js +279 -0
  51. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  52. package/dist/daemon/daemon-usefulness.js +62 -0
  53. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  54. package/dist/{daemon.js → daemon/daemon.js} +105 -48
  55. package/dist/daemon/heartbeat.d.ts +115 -0
  56. package/dist/daemon/heartbeat.js +141 -0
  57. package/dist/daemon/idle-grace.d.ts +32 -0
  58. package/dist/daemon/idle-grace.js +41 -0
  59. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +23 -2
  60. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
  61. package/dist/daemon/port-presence.d.ts +76 -0
  62. package/dist/daemon/port-presence.js +93 -0
  63. package/dist/daemon/state-dir.d.ts +7 -0
  64. package/dist/daemon/state-dir.js +59 -0
  65. package/dist/daemon/unclean-predecessor.d.ts +23 -0
  66. package/dist/daemon/unclean-predecessor.js +27 -0
  67. package/dist/domain/domain-model.js +2 -2
  68. package/dist/events/body-failures.js +8 -8
  69. package/dist/events/contradictions.d.ts +1 -1
  70. package/dist/events/contradictions.js +60 -10
  71. package/dist/events/echo-mismatch.js +10 -10
  72. package/dist/events/event-filters.js +6 -6
  73. package/dist/events/json-salvage.d.ts +2 -1
  74. package/dist/events/json-salvage.js +6 -6
  75. package/dist/events/observed-in-window.d.ts +12 -0
  76. package/dist/events/observed-in-window.js +21 -0
  77. package/dist/events/predicate-eval.d.ts +53 -20
  78. package/dist/events/predicate-eval.js +179 -55
  79. package/dist/events/predicate-parse.d.ts +23 -0
  80. package/dist/events/predicate-parse.js +54 -0
  81. package/dist/events/predicate.d.ts +7 -2
  82. package/dist/events/predicate.js +126 -35
  83. package/dist/events/reconcile.js +5 -5
  84. package/dist/events/ring-buffer.js +2 -17
  85. package/dist/events/stale-response.js +6 -6
  86. package/dist/events/testid-near-miss.d.ts +22 -0
  87. package/dist/events/testid-near-miss.js +33 -0
  88. package/dist/events/unit-mismatch.js +8 -8
  89. package/dist/flows/affected-tools.js +19 -4
  90. package/dist/flows/affected.js +1 -1
  91. package/dist/flows/annotate-target.d.ts +1 -0
  92. package/dist/flows/annotate-target.js +20 -0
  93. package/dist/flows/annotate-tools.js +25 -3
  94. package/dist/flows/annotate.js +17 -4
  95. package/dist/flows/assertion-integrity.d.ts +2 -1
  96. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  97. package/dist/flows/assertion-tiers-store.js +2 -2
  98. package/dist/flows/attributed-failure.d.ts +17 -0
  99. package/dist/flows/attributed-failure.js +20 -0
  100. package/dist/flows/buddy-status.d.ts +2 -1
  101. package/dist/flows/buddy-status.js +2 -2
  102. package/dist/flows/coverage.d.ts +3 -2
  103. package/dist/flows/coverage.js +2 -2
  104. package/dist/flows/decision.js +24 -9
  105. package/dist/flows/empty-flow.d.ts +6 -0
  106. package/dist/flows/empty-flow.js +24 -0
  107. package/dist/flows/flake-store.d.ts +0 -1
  108. package/dist/flows/flake-store.js +1 -1
  109. package/dist/flows/flake.d.ts +2 -1
  110. package/dist/flows/flake.js +2 -2
  111. package/dist/flows/flow-classify.js +12 -0
  112. package/dist/flows/flow-replay-run.js +4 -4
  113. package/dist/flows/flow-replay.d.ts +6 -8
  114. package/dist/flows/flow-replay.js +132 -48
  115. package/dist/flows/flow-report.js +1 -1
  116. package/dist/flows/flow-scope.d.ts +1 -6
  117. package/dist/flows/flow-step-runners.d.ts +26 -6
  118. package/dist/flows/flow-step-runners.js +156 -6
  119. package/dist/flows/flow-success.js +20 -15
  120. package/dist/flows/flow-tools.js +63 -21
  121. package/dist/flows/flows.js +15 -11
  122. package/dist/flows/gate.d.ts +4 -3
  123. package/dist/flows/gate.js +1 -1
  124. package/dist/flows/heal-run.d.ts +0 -8
  125. package/dist/flows/heal-run.js +5 -5
  126. package/dist/flows/heal.js +3 -3
  127. package/dist/flows/predicate-to-expect.d.ts +38 -0
  128. package/dist/flows/predicate-to-expect.js +95 -0
  129. package/dist/flows/replay.d.ts +16 -1
  130. package/dist/flows/replay.js +58 -22
  131. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  132. package/dist/flows/role-anchor-nearest.js +45 -0
  133. package/dist/flows/role-drift-reason.d.ts +20 -0
  134. package/dist/flows/role-drift-reason.js +39 -0
  135. package/dist/flows/server-verify.d.ts +1 -1
  136. package/dist/flows/server-verify.js +5 -5
  137. package/dist/flows/suite-flakes.d.ts +33 -0
  138. package/dist/flows/suite-flakes.js +32 -0
  139. package/dist/flows/verify-change-tools.js +32 -10
  140. package/dist/flows/verify-change-verdict.d.ts +15 -0
  141. package/dist/flows/verify-change-verdict.js +25 -0
  142. package/dist/flows/watch-batcher.d.ts +3 -2
  143. package/dist/honesty/already-true.d.ts +2 -0
  144. package/dist/honesty/already-true.js +31 -0
  145. package/dist/honesty/blind-spots.d.ts +35 -3
  146. package/dist/honesty/blind-spots.js +57 -12
  147. package/dist/honesty/honesty.d.ts +3 -2
  148. package/dist/honesty/honesty.js +4 -4
  149. package/dist/honesty/uncaptured-bodies.js +1 -1
  150. package/dist/honesty/unread-outcome.js +2 -2
  151. package/dist/honesty/verified.d.ts +31 -3
  152. package/dist/honesty/verified.js +64 -8
  153. package/dist/http-server.js +23 -7
  154. package/dist/hunt/hunt-report.d.ts +2 -1
  155. package/dist/hunt/hunt-report.js +2 -2
  156. package/dist/index.d.ts +9 -6
  157. package/dist/index.js +41 -25
  158. package/dist/init/agent-rules.d.ts +20 -1
  159. package/dist/init/agent-rules.js +64 -8
  160. package/dist/init/app-choice.d.ts +20 -0
  161. package/dist/init/app-choice.js +25 -0
  162. package/dist/init/astro-host.d.ts +21 -0
  163. package/dist/init/astro-host.js +27 -0
  164. package/dist/init/astro-patch.d.ts +16 -0
  165. package/dist/init/astro-patch.js +240 -0
  166. package/dist/init/capabilities.js +4 -1
  167. package/dist/init/cra.d.ts +50 -0
  168. package/dist/init/cra.js +106 -0
  169. package/dist/init/cursor.js +39 -4
  170. package/dist/init/desktop-doctor.d.ts +3 -2
  171. package/dist/init/desktop-doctor.js +4 -4
  172. package/dist/init/detect.d.ts +9 -2
  173. package/dist/init/detect.js +24 -5
  174. package/dist/init/mcp-clients.d.ts +81 -0
  175. package/dist/init/mcp-clients.js +248 -0
  176. package/dist/init/mcp-registered.d.ts +12 -0
  177. package/dist/init/mcp-registered.js +14 -0
  178. package/dist/init/mcp.d.ts +27 -2
  179. package/dist/init/mcp.js +40 -4
  180. package/dist/init/next-patch.d.ts +2 -4
  181. package/dist/init/next-patch.js +109 -19
  182. package/dist/init/node-io.js +29 -6
  183. package/dist/init/plan-framework.d.ts +16 -1
  184. package/dist/init/plan-framework.js +166 -8
  185. package/dist/init/plan.d.ts +85 -6
  186. package/dist/init/plan.js +196 -33
  187. package/dist/init/project-id.d.ts +5 -9
  188. package/dist/init/project-id.js +10 -22
  189. package/dist/init/run.d.ts +23 -1
  190. package/dist/init/run.js +338 -47
  191. package/dist/init/slash-command.d.ts +9 -0
  192. package/dist/init/slash-command.js +10 -1
  193. package/dist/init/snippets.d.ts +7 -10
  194. package/dist/init/snippets.js +44 -15
  195. package/dist/init/vite-config.js +1 -1
  196. package/dist/init/windows-quote.d.ts +38 -0
  197. package/dist/init/windows-quote.js +70 -0
  198. package/dist/init/workspace-apps.d.ts +23 -0
  199. package/dist/init/workspace-apps.js +68 -0
  200. package/dist/input/driven-redaction.js +1 -1
  201. package/dist/input/network-detail.js +8 -8
  202. package/dist/input/network-mock-tools.js +2 -2
  203. package/dist/input/network-mock.js +4 -4
  204. package/dist/input/real-input.js +8 -3
  205. package/dist/input/scroll-find.js +2 -2
  206. package/dist/input/scroll-tools.d.ts +0 -5
  207. package/dist/input/scroll-tools.js +11 -4
  208. package/dist/input/viewport-tools.js +2 -2
  209. package/dist/journal/ambient-store.d.ts +0 -2
  210. package/dist/journal/ambient-store.js +1 -1
  211. package/dist/journal/ambient.js +1 -1
  212. package/dist/journal/attach-journal.d.ts +2 -1
  213. package/dist/journal/deviation-report.js +6 -6
  214. package/dist/journal/deviation-service.js +1 -1
  215. package/dist/journal/envelope-store.d.ts +0 -2
  216. package/dist/journal/envelope-store.js +1 -1
  217. package/dist/journal/envelope.d.ts +2 -3
  218. package/dist/journal/envelope.js +3 -3
  219. package/dist/journal/journal-query.js +1 -1
  220. package/dist/journal/journal-recorder.d.ts +2 -1
  221. package/dist/journal/journal-recorder.js +2 -2
  222. package/dist/journal/rollups.js +9 -9
  223. package/dist/journal/session-end.d.ts +2 -1
  224. package/dist/journal/session-journal.js +4 -4
  225. package/dist/license/license.d.ts +0 -14
  226. package/dist/license/license.js +8 -8
  227. package/dist/log.d.ts +10 -0
  228. package/dist/log.js +11 -1
  229. package/dist/mcp/mcp-is-error.d.ts +13 -0
  230. package/dist/mcp/mcp-is-error.js +18 -0
  231. package/dist/mcp/mcp-outage.d.ts +32 -0
  232. package/dist/mcp/mcp-outage.js +55 -0
  233. package/dist/mcp/mcp-proxy.d.ts +134 -0
  234. package/dist/mcp/mcp-proxy.js +720 -0
  235. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  236. package/dist/{mcp.js → mcp/mcp.js} +139 -37
  237. package/dist/mcp/proxy-handshake.d.ts +3 -0
  238. package/dist/mcp/proxy-handshake.js +54 -0
  239. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  240. package/dist/mcp/proxy-lifecycle.js +70 -0
  241. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  242. package/dist/mcp/tool-catalog-cache.js +55 -0
  243. package/dist/oracles/propose-consequences.d.ts +2 -1
  244. package/dist/oracles/propose-consequences.js +16 -11
  245. package/dist/oracles/self-instrument.d.ts +2 -1
  246. package/dist/oracles/self-instrument.js +1 -1
  247. package/dist/phenomena/phenomena.js +3 -3
  248. package/dist/platform.d.ts +16 -0
  249. package/dist/platform.js +16 -0
  250. package/dist/pool/browser-pool.d.ts +9 -0
  251. package/dist/pool/browser-pool.js +38 -9
  252. package/dist/pool/playwright-launcher.d.ts +15 -2
  253. package/dist/pool/playwright-launcher.js +21 -2
  254. package/dist/project/fs-port.js +1 -1
  255. package/dist/project/project-tools.js +2 -2
  256. package/dist/runs/build-verification-run.js +1 -1
  257. package/dist/runs/repair-prompt.d.ts +0 -6
  258. package/dist/runs/repair-prompt.js +3 -3
  259. package/dist/runs/risk-classify.js +1 -1
  260. package/dist/runs/run-diff.d.ts +0 -2
  261. package/dist/runs/run-diff.js +3 -3
  262. package/dist/runs/run-tools.js +2 -2
  263. package/dist/runs/verification-sync.js +2 -2
  264. package/dist/runs/verify-http.js +1 -1
  265. package/dist/runs/verify-server.js +1 -1
  266. package/dist/session/attachment-history.d.ts +46 -0
  267. package/dist/session/attachment-history.js +65 -0
  268. package/dist/session/command-timeout.d.ts +11 -3
  269. package/dist/session/command-timeout.js +2 -2
  270. package/dist/session/control-envelope.d.ts +6 -0
  271. package/dist/session/control-envelope.js +7 -1
  272. package/dist/session/dev-server-probe.d.ts +13 -0
  273. package/dist/session/dev-server-probe.js +52 -0
  274. package/dist/session/last-act.d.ts +17 -3
  275. package/dist/session/last-act.js +16 -4
  276. package/dist/session/live-control-tools.js +7 -2
  277. package/dist/session/live-control.js +1 -1
  278. package/dist/session/looks-like-dev-server.d.ts +14 -0
  279. package/dist/session/looks-like-dev-server.js +25 -0
  280. package/dist/session/no-session-diagnosis.d.ts +45 -0
  281. package/dist/session/no-session-diagnosis.js +120 -0
  282. package/dist/session/no-session-watch.d.ts +21 -0
  283. package/dist/session/no-session-watch.js +69 -0
  284. package/dist/session/observed-state.d.ts +4 -0
  285. package/dist/session/observed-state.js +19 -1
  286. package/dist/session/output-budget.js +1 -1
  287. package/dist/session/pending-commands.js +1 -0
  288. package/dist/session/session-health.d.ts +8 -1
  289. package/dist/session/session-health.js +8 -7
  290. package/dist/session/session-info.d.ts +43 -8
  291. package/dist/session/session-info.js +46 -1
  292. package/dist/session/session-lease.d.ts +0 -11
  293. package/dist/session/session-lease.js +1 -1
  294. package/dist/session/session-manager.d.ts +6 -0
  295. package/dist/session/session-manager.js +124 -12
  296. package/dist/session/session-readiness.d.ts +1 -1
  297. package/dist/session/session-readiness.js +6 -1
  298. package/dist/session/session-reaper.js +1 -1
  299. package/dist/session/session-reconnect.d.ts +32 -0
  300. package/dist/session/session-reconnect.js +42 -0
  301. package/dist/session/session.d.ts +15 -2
  302. package/dist/session/session.js +77 -26
  303. package/dist/session/state-select.d.ts +1 -1
  304. package/dist/session/state-select.js +1 -1
  305. package/dist/status-payload.d.ts +19 -0
  306. package/dist/status-payload.js +11 -0
  307. package/dist/telemetry/argument-shape.js +10 -1
  308. package/dist/telemetry/browser-mode.d.ts +27 -0
  309. package/dist/telemetry/browser-mode.js +33 -0
  310. package/dist/telemetry/bug-found.d.ts +17 -21
  311. package/dist/telemetry/bug-found.js +120 -8
  312. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  313. package/dist/telemetry/cli-telemetry.js +27 -4
  314. package/dist/telemetry/crash-cause.d.ts +47 -0
  315. package/dist/telemetry/crash-cause.js +104 -0
  316. package/dist/telemetry/daemon-telemetry.d.ts +38 -1
  317. package/dist/telemetry/daemon-telemetry.js +37 -12
  318. package/dist/telemetry/dev-repo.js +4 -4
  319. package/dist/telemetry/error-class.d.ts +26 -0
  320. package/dist/telemetry/error-class.js +52 -0
  321. package/dist/telemetry/error-fingerprint.js +21 -2
  322. package/dist/telemetry/feedback-cli.d.ts +8 -3
  323. package/dist/telemetry/feedback-cli.js +52 -8
  324. package/dist/telemetry/feedback-context.d.ts +23 -6
  325. package/dist/telemetry/feedback-context.js +105 -5
  326. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  327. package/dist/telemetry/feedback-delivery.js +30 -0
  328. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  329. package/dist/telemetry/feedback-outbox.js +87 -0
  330. package/dist/telemetry/feedback.d.ts +22 -1
  331. package/dist/telemetry/feedback.js +67 -8
  332. package/dist/telemetry/git-facts.d.ts +7 -0
  333. package/dist/telemetry/git-facts.js +5 -5
  334. package/dist/telemetry/project-profile.js +2 -1
  335. package/dist/telemetry/session-metrics.d.ts +47 -3
  336. package/dist/telemetry/session-metrics.js +315 -15
  337. package/dist/telemetry/telemetry.d.ts +45 -2
  338. package/dist/telemetry/telemetry.js +150 -37
  339. package/dist/telemetry/verification-of.d.ts +22 -0
  340. package/dist/telemetry/verification-of.js +84 -0
  341. package/dist/tools/act-capsule.js +2 -2
  342. package/dist/tools/act-danger.d.ts +12 -0
  343. package/dist/tools/act-danger.js +20 -3
  344. package/dist/tools/act-helpers.js +4 -4
  345. package/dist/tools/act-tools.d.ts +0 -8
  346. package/dist/tools/act-tools.js +114 -118
  347. package/dist/tools/act-view.js +7 -7
  348. package/dist/tools/alias-args.d.ts +17 -0
  349. package/dist/tools/alias-args.js +25 -0
  350. package/dist/tools/assert-grade.js +9 -9
  351. package/dist/tools/assert-verdict.d.ts +9 -1
  352. package/dist/tools/assert-verdict.js +21 -6
  353. package/dist/tools/browser-tools.js +36 -11
  354. package/dist/tools/contract-tools.d.ts +0 -5
  355. package/dist/tools/contract-tools.js +27 -1
  356. package/dist/tools/coverage-identity.d.ts +34 -0
  357. package/dist/tools/coverage-identity.js +46 -0
  358. package/dist/tools/coverage-tools.js +10 -14
  359. package/dist/tools/dynamic-tools.d.ts +2 -1
  360. package/dist/tools/dynamic-tools.js +96 -19
  361. package/dist/tools/error-recovery.d.ts +34 -2
  362. package/dist/tools/error-recovery.js +255 -4
  363. package/dist/tools/feedback-invite.d.ts +48 -0
  364. package/dist/tools/feedback-invite.js +71 -0
  365. package/dist/tools/feedback-tools.d.ts +12 -1
  366. package/dist/tools/feedback-tools.js +28 -5
  367. package/dist/tools/invoke-tool.d.ts +0 -12
  368. package/dist/tools/invoke-tool.js +126 -33
  369. package/dist/tools/lease-hint.d.ts +17 -0
  370. package/dist/tools/lease-hint.js +24 -0
  371. package/dist/tools/lease-tools.js +6 -5
  372. package/dist/tools/merge-tools.d.ts +10 -1
  373. package/dist/tools/merge-tools.js +9 -3
  374. package/dist/tools/merged-name-redirect.d.ts +13 -0
  375. package/dist/tools/merged-name-redirect.js +55 -0
  376. package/dist/tools/navigate-arrival.d.ts +26 -0
  377. package/dist/tools/navigate-arrival.js +60 -0
  378. package/dist/tools/navigate-result.d.ts +31 -0
  379. package/dist/tools/navigate-result.js +38 -0
  380. package/dist/tools/observe-tools.js +72 -20
  381. package/dist/tools/observed-nothing.d.ts +25 -0
  382. package/dist/tools/observed-nothing.js +33 -0
  383. package/dist/tools/query-paginate.js +2 -2
  384. package/dist/tools/query-shape.d.ts +18 -0
  385. package/dist/tools/query-shape.js +39 -0
  386. package/dist/tools/read-tools.js +81 -20
  387. package/dist/tools/real-input-attempt.d.ts +28 -0
  388. package/dist/tools/real-input-attempt.js +93 -0
  389. package/dist/tools/reconcile-tools.js +7 -7
  390. package/dist/tools/reload-result.d.ts +14 -0
  391. package/dist/tools/reload-result.js +29 -0
  392. package/dist/tools/snapshot-delta.d.ts +4 -5
  393. package/dist/tools/snapshot-delta.js +0 -0
  394. package/dist/tools/tool-kit.d.ts +16 -0
  395. package/dist/tools/tool-surface.d.ts +83 -0
  396. package/dist/tools/tool-surface.js +217 -0
  397. package/dist/tools/tools-helpers.js +6 -6
  398. package/dist/tools/tools.d.ts +15 -0
  399. package/dist/tools/tools.js +53 -17
  400. package/dist/tools/unadvertised-help.d.ts +5 -0
  401. package/dist/tools/unadvertised-help.js +39 -0
  402. package/dist/trace.d.ts +69 -0
  403. package/dist/trace.js +150 -0
  404. package/dist/update/nudge-credit.d.ts +4 -0
  405. package/dist/update/nudge-credit.js +53 -0
  406. package/dist/update/sdk-sync.d.ts +21 -0
  407. package/dist/update/sdk-sync.js +42 -0
  408. package/dist/update/update-checker.js +3 -3
  409. package/dist/update/update-nudge.d.ts +63 -6
  410. package/dist/update/update-nudge.js +128 -15
  411. package/dist/update/updater.d.ts +1 -9
  412. package/dist/update/updater.js +13 -8
  413. package/dist/version/peer-announce.d.ts +4 -0
  414. package/dist/version/peer-announce.js +30 -0
  415. package/dist/{server-version.js → version/server-version.js} +5 -1
  416. package/dist/version/version-nudge.d.ts +28 -0
  417. package/dist/version/version-nudge.js +40 -0
  418. package/dist/version/version-skew.d.ts +66 -0
  419. package/dist/version/version-skew.js +71 -0
  420. package/dist/visual/visual-diff.js +2 -2
  421. package/dist/visual/visual-tools.js +4 -4
  422. package/docs/README.md +55 -0
  423. package/docs/agent-cheatsheet.md +85 -0
  424. package/docs/architecture.md +113 -0
  425. package/docs/benchmarks.md +255 -0
  426. package/docs/debugging.md +102 -0
  427. package/docs/deploy-checks.md +69 -0
  428. package/docs/desktop-apps.md +256 -0
  429. package/docs/enterprise.md +62 -0
  430. package/docs/fixtures-dispatch-receiver.yml +76 -0
  431. package/docs/fixtures.md +45 -0
  432. package/docs/flows.md +166 -0
  433. package/docs/gate-plan.md +98 -0
  434. package/docs/gates.md +117 -0
  435. package/docs/getting-started.md +469 -0
  436. package/docs/human-control.md +63 -0
  437. package/docs/integration-patterns.md +171 -0
  438. package/docs/local-registry.md +126 -0
  439. package/docs/matrix/2.5.0/claude-code-machine.json +19 -0
  440. package/docs/matrix/2.5.0/codex-machine.json +20 -0
  441. package/docs/matrix/2.5.0/cursor-machine.json +22 -0
  442. package/docs/matrix/2.5.0/gemini-machine.json +22 -0
  443. package/docs/matrix/2.5.0/opencode-machine.json +22 -0
  444. package/docs/matrix/2.5.0/vscode-machine.json +22 -0
  445. package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
  446. package/docs/matrix/MATRIX.md +26 -0
  447. package/docs/matrix/README.md +59 -0
  448. package/docs/mint.json +61 -0
  449. package/docs/multi-agent-testing.md +55 -0
  450. package/docs/platform-integration.md +122 -0
  451. package/docs/system-map.md +152 -0
  452. package/docs/telemetry-contract.md +214 -0
  453. package/docs/telemetry.md +191 -0
  454. package/docs/testing.md +82 -0
  455. package/docs/token-efficiency.md +80 -0
  456. package/docs/usage.md +1046 -0
  457. package/package.json +8 -5
  458. package/dist/cli-doctor.d.ts +0 -6
  459. package/dist/cli-doctor.js +0 -60
  460. package/dist/cli-launch.js +0 -112
  461. package/dist/daemon-resilience.d.ts +0 -22
  462. package/dist/daemon-resilience.js +0 -85
  463. package/dist/mcp-proxy.d.ts +0 -67
  464. package/dist/mcp-proxy.js +0 -337
  465. package/dist/tools/profiles.d.ts +0 -37
  466. package/dist/tools/profiles.js +0 -139
  467. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  468. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  469. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  470. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  471. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  472. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -2,7 +2,8 @@ import { LastAct } from './last-act.js';
2
2
  import { commandTimeoutMessage } from './command-timeout.js';
3
3
  import { readHealthEvent } from './session-health.js';
4
4
  import { PendingCommands } from './pending-commands.js';
5
- import { EventType, HumanControlDataSchema, HumanControlKind, HumanMarkDataSchema, ReticleCommand, MessageKind, parseEventPayload, PresenterTone, SESSION_HEALTH, SESSION_LEASE, SESSION_LIFECYCLE, SessionState, } from '@reticlehq/core';
5
+ import { span } from '../trace.js';
6
+ import { AppRuntime, EventType, HumanControlDataSchema, HumanControlKind, HumanMarkDataSchema, ReticleCommand, MessageKind, parseEventPayload, PresenterTone, SESSION_HEALTH, SESSION_LEASE, SESSION_LIFECYCLE, SessionState, } from '@reticlehq/core';
6
7
  import { RingBuffer } from '../events/ring-buffer.js';
7
8
  import { filterEvents, mergeEventsBySeq, } from '../journal/journal-query.js';
8
9
  import {} from '../journal/ambient.js';
@@ -13,6 +14,7 @@ import { ReviewStore } from './review-store.js';
13
14
  import { buildSessionRecommendation } from './session-recommendation.js';
14
15
  import { buildPresenterArgs } from './presenter-args.js';
15
16
  import { buildSessionLease } from './session-lease.js';
17
+ import { buildSessionInfo } from './session-info.js';
16
18
  const DEFAULT_COMMAND_TIMEOUT_MS = 8000;
17
19
  /** Prefix on correlated command ids (c1, c2, …) — distinguishes them from mark ids. */
18
20
  const COMMAND_ID_PREFIX = 'c';
@@ -32,6 +34,8 @@ export class Session {
32
34
  title;
33
35
  adapters;
34
36
  hasCapabilities;
37
+ /** Set when the page's SDK version differs from the daemon's (see version-skew.ts). */
38
+ versionSkew;
35
39
  /**
36
40
  * Extra key names this app declared sensitive via `connect({ redact: { keys } })`. Held so the
37
41
  * DRIVEN path can redact them too — a request body the daemon captures from the network stack
@@ -44,12 +48,15 @@ export class Session {
44
48
  #buffer = new RingBuffer();
45
49
  #pending = new PendingCommands();
46
50
  #listeners = new Set();
51
+ #disconnectListeners = new Set();
47
52
  #actionSeq = 0;
48
53
  #lastSeenAt;
49
54
  #hidden = false;
50
55
  /** Which shell the page reported (PAGE_HEALTH). Undefined until the first report lands. */
51
56
  #runtime;
52
57
  #engine;
58
+ /** Which browser the page said it is (chrome/edge/arc/…). Undefined on an SDK too old to report one. */
59
+ #brand;
53
60
  #focused = true;
54
61
  /** Liveness: wall-clock of the last AGENT command (distinct from browser chatter / lastSeen). */
55
62
  #lastAgentActivityAt;
@@ -101,14 +108,18 @@ export class Session {
101
108
  * `runtime` is optional so an older SDK (which does not report it) still works — it simply loses
102
109
  * the desktop-specific timeout diagnosis rather than breaking.
103
110
  */
104
- applyHealth(hidden, focused, runtime, engine) {
111
+ applyHealth(hidden, focused, runtime, engine, brand) {
105
112
  this.#hidden = hidden;
106
113
  this.#focused = focused;
107
- if (runtime === 'electron' || runtime === 'tauri' || runtime === 'web') {
114
+ if (AppRuntime.ELECTRON === runtime ||
115
+ AppRuntime.TAURI === runtime ||
116
+ AppRuntime.WEB === runtime) {
108
117
  this.#runtime = runtime;
109
118
  }
110
119
  if (engine !== undefined)
111
120
  this.#engine = engine;
121
+ if (brand !== undefined)
122
+ this.#brand = brand;
112
123
  }
113
124
  /** The shell (web/electron/tauri) and rendering engine the SDK reported. Undefined on an older SDK. */
114
125
  get runtime() {
@@ -117,6 +128,10 @@ export class Session {
117
128
  get engine() {
118
129
  return this.#engine;
119
130
  }
131
+ /** The browser brand the SDK reported. Absent (never guessed) when the page did not say. */
132
+ get brand() {
133
+ return this.#brand;
134
+ }
120
135
  /** Throttled if the tab is hidden OR we have not heard from it recently. */
121
136
  throttled() {
122
137
  return this.#hidden || this.lastSeenMs() > SESSION_HEALTH.STALE_THRESHOLD_MS;
@@ -137,29 +152,19 @@ export class Session {
137
152
  return recommendation === undefined ? base : { ...base, recommendation };
138
153
  }
139
154
  info() {
140
- const base = {
141
- sessionId: this.id,
155
+ return buildSessionInfo({
156
+ id: this.id,
142
157
  url: this.url,
143
- ...(this.projectId === undefined ? {} : { projectId: this.projectId }),
158
+ projectId: this.projectId,
144
159
  title: this.title,
145
160
  adapters: this.adapters,
146
161
  hasCapabilities: this.hasCapabilities,
162
+ versionSkew: this.versionSkew,
147
163
  hidden: this.#hidden,
148
- ...this.health(),
149
- };
150
- if (this.staleMs() > SESSION_LEASE.STALE_AFTER_MS) {
151
- base.stale = true;
152
- base.cleanup_suggestion =
153
- 'Call reticle_session{action:"end"} to free this session before starting new work.';
154
- }
155
- // Surface human bug reports in reticle_sessions (only when > 0, so a clean session adds nothing).
156
- const marks = this.#review.pendingCount();
157
- if (marks > 0) {
158
- base.pendingMarks = marks;
159
- const s = marks === 1 ? '' : 's';
160
- base.review_suggestion = `The human flagged ${String(marks)} issue${s} on this tab — call reticle_session{action:"review"} to see and fix ${marks === 1 ? 'it' : 'them'}.`;
161
- }
162
- return base;
164
+ health: () => this.health(),
165
+ staleMs: () => this.staleMs(),
166
+ pendingMarkCount: () => this.#review.pendingCount(),
167
+ });
163
168
  }
164
169
  /** Wall-clock age of the session in milliseconds. */
165
170
  staleMs() {
@@ -169,7 +174,7 @@ export class Session {
169
174
  pushEvent(event, byteSize) {
170
175
  if (event.type === EventType.PAGE_HEALTH) {
171
176
  const r = readHealthEvent(event.data);
172
- this.applyHealth(r.hidden ?? this.#hidden, r.focused ?? this.#focused, r.runtime, r.engine);
177
+ this.applyHealth(r.hidden ?? this.#hidden, r.focused ?? this.#focused, r.runtime, r.engine, r.brand);
173
178
  }
174
179
  // The in-page half of Reticle reporting its own failure — recorded like a tool error
175
180
  // (fingerprinted, variables stripped) so a broken observer is as visible as a broken tool.
@@ -195,7 +200,7 @@ export class Session {
195
200
  // pushState/replaceState/popstate; without this the URL stays frozen at the hello value, and
196
201
  // URL-based CDP correlation (real input) silently breaks after the first client-side nav.
197
202
  const to = event.data['to'];
198
- if (typeof to === 'string' && to.length > 0)
203
+ if ('string' === typeof to && to.length > 0)
199
204
  this.url = to;
200
205
  }
201
206
  const t = this.elapsed();
@@ -218,6 +223,35 @@ export class Session {
218
223
  recordActedRef(ref) {
219
224
  this.#observed.recordActedRef(ref);
220
225
  }
226
+ actedLabels() {
227
+ return this.#observed.actedLabels();
228
+ }
229
+ /**
230
+ * Derive the snapshot-shaped label (`button "Deploy"`) from an act reply and remember it.
231
+ *
232
+ * Same shape the interactive snapshot emits, so coverage can match a control the agent drove
233
+ * against the same control after a re-render gave it a different ref.
234
+ */
235
+ recordActedLabelFrom(result) {
236
+ const payload = result.result;
237
+ if (typeof payload !== 'object' || null === payload)
238
+ return;
239
+ const record = payload;
240
+ // The TESTID first: it is the strongest identity a control has and it survives any re-render.
241
+ // Coverage previously matched only on `role "name"`, so a control with a testid but no accessible
242
+ // name — or on a stack where the act reply carried neither — was unrecognisable after a
243
+ // re-render, and coverage read `exercised: 0` however much work had been done.
244
+ const testid = record['testid'];
245
+ if ('string' === typeof testid && testid.length > 0)
246
+ this.#observed.recordActedLabel(testid);
247
+ const role = record['role'];
248
+ const name = record['name'];
249
+ if (typeof role !== 'string' || typeof name !== 'string')
250
+ return;
251
+ if (0 === role.length || 0 === name.length)
252
+ return;
253
+ this.#observed.recordActedLabel(`${role} "${name}"`);
254
+ }
221
255
  actedRefs() {
222
256
  return this.#observed.actedRefs();
223
257
  }
@@ -340,6 +374,10 @@ export class Session {
340
374
  this.#listeners.add(listener);
341
375
  return () => this.#listeners.delete(listener);
342
376
  }
377
+ onDisconnect(listener) {
378
+ this.#disconnectListeners.add(listener);
379
+ return () => this.#disconnectListeners.delete(listener);
380
+ }
343
381
  /** Send a command to the browser and await its reply (or time out). */
344
382
  command(name, args = {}, timeoutMs = DEFAULT_COMMAND_TIMEOUT_MS) {
345
383
  // Recorded HERE rather than at the tool call sites: this is the one place every driven action
@@ -348,7 +386,7 @@ export class Session {
348
386
  // still wrong, and it made the number depend on which tool the agent happened to use.
349
387
  if (name === ReticleCommand.ACT) {
350
388
  const ref = args['ref'];
351
- if (typeof ref === 'string')
389
+ if ('string' === typeof ref)
352
390
  this.recordActedRef(ref);
353
391
  }
354
392
  const id = this.#pending.nextId(COMMAND_ID_PREFIX);
@@ -369,7 +407,17 @@ export class Session {
369
407
  }));
370
408
  this.#socket.send(payload);
371
409
  const sentAt = Date.now(); // browser-leg timing; see recordBrowserLatency for why it is split out
372
- return awaited.finally(() => recordBrowserLatency(Date.now() - sentAt));
410
+ // Traced because this is the boundary that decides whether a slow tool call is OUR overhead or
411
+ // the app taking its time — the split telemetry reports in aggregate, made visible per call.
412
+ return span('browser.command', { command: name, sessionId: this.id }, () => awaited)
413
+ .then((result) => {
414
+ // The label is only knowable from the REPLY — the request carries a ref, and a ref dies with
415
+ // the next re-render. Recorded here so coverage survives frameworks that replace nodes.
416
+ if (name === ReticleCommand.ACT)
417
+ this.recordActedLabelFrom(result);
418
+ return result;
419
+ })
420
+ .finally(() => recordBrowserLatency(Date.now() - sentAt));
373
421
  }
374
422
  handleResult(result) {
375
423
  this.#pending.settle(result);
@@ -377,6 +425,9 @@ export class Session {
377
425
  /** Reject everything still in flight — used on disconnect. */
378
426
  rejectAll(reason) {
379
427
  this.#pending.rejectAll(reason);
428
+ for (const listener of this.#disconnectListeners)
429
+ listener();
430
+ this.#disconnectListeners.clear();
380
431
  }
381
432
  /** End this transport without letting a stale socket remove its replacement session. */
382
433
  disconnect(reason) {
@@ -490,7 +541,7 @@ export class Session {
490
541
  // still wrong, and it made the number depend on which tool the agent happened to use.
491
542
  if (name === ReticleCommand.ACT) {
492
543
  const ref = args['ref'];
493
- if (typeof ref === 'string')
544
+ if ('string' === typeof ref)
494
545
  this.recordActedRef(ref);
495
546
  }
496
547
  const id = this.#pending.nextId(COMMAND_ID_PREFIX);
@@ -4,4 +4,4 @@
4
4
  * without paying the truncation tax); the server re-exports it for its back-compat fallback path
5
5
  * (an older browser that returns the whole store) and for the predicate engine's `state` assertion.
6
6
  */
7
- export { selectPath, capDepth, type PathSelection } from '@reticlehq/core';
7
+ export { selectPath, capDepth, projectComponentState, type PathSelection } from '@reticlehq/core';
@@ -4,4 +4,4 @@
4
4
  * without paying the truncation tax); the server re-exports it for its back-compat fallback path
5
5
  * (an older browser that returns the whole store) and for the predicate engine's `state` assertion.
6
6
  */
7
- export { selectPath, capDepth } from '@reticlehq/core';
7
+ export { selectPath, capDepth, projectComponentState } from '@reticlehq/core';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * What `GET /status` answers — the daemon describing itself.
3
+ *
4
+ * Carries the daemon's OWN version, because a daemon outlives every agent attached to it: after an
5
+ * upgrade the new CLI attaches to the old daemon and serves its code, and until this field existed
6
+ * there was no surface anywhere — not /status, not `reticle status` — naming the version actually
7
+ * answering requests. See describeDaemonSkew.
8
+ */
9
+ import type { SessionInfo } from './session/session-info.js';
10
+ interface StatusPayload {
11
+ running: true;
12
+ version: string;
13
+ /** The wire contract this daemon speaks — what another process compares against, not the version. */
14
+ contract: string;
15
+ sessionCount: number;
16
+ sessions: SessionInfo[];
17
+ }
18
+ export declare function statusPayload(sessionCount: number, sessions: SessionInfo[]): StatusPayload;
19
+ export {};
@@ -0,0 +1,11 @@
1
+ import { CONTRACT_FINGERPRINT } from '@reticlehq/core';
2
+ import { SERVER_VERSION } from './version/server-version.js';
3
+ export function statusPayload(sessionCount, sessions) {
4
+ return {
5
+ running: true,
6
+ version: SERVER_VERSION,
7
+ contract: CONTRACT_FINGERPRINT,
8
+ sessionCount,
9
+ sessions,
10
+ };
11
+ }
@@ -57,10 +57,19 @@ export function describeParam(name, value) {
57
57
  return name;
58
58
  return `${name}:${allowed.has(value) ? value : OTHER_VALUE}`;
59
59
  }
60
+ /**
61
+ * Parameters that carry no information about how the tool is USED, and so are not counted.
62
+ *
63
+ * `sessionId` is accepted by nearly every tool and passed on nearly every call, so counting it
64
+ * produced a row per tool saying only "the agent addressed a session" — in one real day's export it
65
+ * was a third of the toolParams block. This histogram exists to answer "is anyone using the argument
66
+ * we shipped last release", and a parameter every call carries can never answer it.
67
+ */
68
+ const UNINFORMATIVE_PARAMS = new Set(['sessionId']);
60
69
  /** Every parameter an agent actually passed to one tool call, reduced to names (+ safe enums). */
61
70
  export function describeToolParams(args) {
62
71
  return Object.keys(args)
63
- .filter((key) => args[key] !== undefined)
72
+ .filter((key) => args[key] !== undefined && !UNINFORMATIVE_PARAMS.has(key))
64
73
  .map((key) => describeParam(key, args[key]))
65
74
  .sort();
66
75
  }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Was this verification watched by a person, or run by a machine?
3
+ *
4
+ * A verification's cost and its meaning both depend on it. A headless run is CI or an unattended
5
+ * agent loop — it should be fast, and there is nobody to notice a wrong answer. A headed run has a
6
+ * human in front of it. And the most common case of all is neither: the SDK in somebody's own dev
7
+ * server, in their own browser, which Reticle never launched at all.
8
+ *
9
+ * Recorded once at daemon start, because that is when the decision is made and it does not change
10
+ * for the daemon's life. Defaults to ATTACHED, which is the honest answer when Reticle launched
11
+ * nothing: claiming "headless" for a browser we did not open would be a guess about somebody's
12
+ * machine.
13
+ */
14
+ export declare const BrowserMode: {
15
+ /** Reticle launched the browser with no display — CI, or an unattended agent loop. */
16
+ readonly HEADLESS: "headless";
17
+ /** Reticle launched a visible browser — a human is watching. */
18
+ readonly HEADED: "headed";
19
+ /** Reticle launched nothing: the SDK connected from a browser somebody else opened. */
20
+ readonly ATTACHED: "attached";
21
+ };
22
+ export type BrowserMode = (typeof BrowserMode)[keyof typeof BrowserMode];
23
+ /** Record how this daemon drives a browser. Called once, at start. */
24
+ export declare function setBrowserMode(mode: BrowserMode): void;
25
+ export declare function getBrowserMode(): BrowserMode;
26
+ /** Tests only — restore the default so cases do not leak into each other. */
27
+ export declare function resetBrowserMode(): void;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Was this verification watched by a person, or run by a machine?
3
+ *
4
+ * A verification's cost and its meaning both depend on it. A headless run is CI or an unattended
5
+ * agent loop — it should be fast, and there is nobody to notice a wrong answer. A headed run has a
6
+ * human in front of it. And the most common case of all is neither: the SDK in somebody's own dev
7
+ * server, in their own browser, which Reticle never launched at all.
8
+ *
9
+ * Recorded once at daemon start, because that is when the decision is made and it does not change
10
+ * for the daemon's life. Defaults to ATTACHED, which is the honest answer when Reticle launched
11
+ * nothing: claiming "headless" for a browser we did not open would be a guess about somebody's
12
+ * machine.
13
+ */
14
+ export const BrowserMode = {
15
+ /** Reticle launched the browser with no display — CI, or an unattended agent loop. */
16
+ HEADLESS: 'headless',
17
+ /** Reticle launched a visible browser — a human is watching. */
18
+ HEADED: 'headed',
19
+ /** Reticle launched nothing: the SDK connected from a browser somebody else opened. */
20
+ ATTACHED: 'attached',
21
+ };
22
+ let current = BrowserMode.ATTACHED;
23
+ /** Record how this daemon drives a browser. Called once, at start. */
24
+ export function setBrowserMode(mode) {
25
+ current = mode;
26
+ }
27
+ export function getBrowserMode() {
28
+ return current;
29
+ }
30
+ /** Tests only — restore the default so cases do not leak into each other. */
31
+ export function resetBrowserMode() {
32
+ current = BrowserMode.ATTACHED;
33
+ }
@@ -1,20 +1,3 @@
1
- /**
2
- * Counting the defects Reticle actually finds.
3
- *
4
- * Every other metric in this product measures whether Reticle is USED. This one measures whether it
5
- * WORKS — and it is the only number that can honestly be put in front of an investor or published,
6
- * because it counts outcomes for users rather than activity by them.
7
- *
8
- * It is read off tool RESULTS at the single chokepoint every tool already crosses, rather than
9
- * emitted from the four places that produce findings. That is deliberate: the detectors are pure
10
- * functions and should stay that way, and reading results means a fifth source of findings is covered
11
- * the day it starts returning one of these shapes, instead of the day someone remembers to instrument
12
- * it.
13
- *
14
- * What is sent is the KIND from Reticle's own findings vocabulary — `signal-contradicted`,
15
- * `console-error`, `duplicate-request`. Never a selector, a URL, an element, or a description of
16
- * anyone's app: that a class of defect was found, never what it was found in.
17
- */
18
1
  import { type BugFound } from '@reticlehq/core';
19
2
  /**
20
3
  * A defect as the CLASSIFIER sees it — everything except `repeat`.
@@ -24,10 +7,23 @@ import { type BugFound } from '@reticlehq/core';
24
7
  */
25
8
  export type BugCandidate = Omit<BugFound, 'repeat'>;
26
9
  /**
27
- * The defects in one tool result.
10
+ * `bug.attribution` was REMOVED, deliberately, and this note is here so it is not re-added casually.
11
+ *
12
+ * It shipped twice and was wrong both times. First it stamped `app` on every contradiction, including
13
+ * `request-never-settled` — which Reticle raises from the ABSENCE of a settle event, so a dev-overlay
14
+ * poll manufactured it with the app doing nothing wrong. That was fixed, and a real drive then found
15
+ * it still inverted: across two full runs, EVERY `attribution: 'app'` was a misattribution, while the
16
+ * one defect that genuinely was a bad agent predicate carried none. One session would have published
17
+ * "2 defects in the app" against a true count of 0.
18
+ *
19
+ * A metric that is confidently wrong about WHOSE fault a defect is, is worse than no metric: it is
20
+ * the number a founder steers on, and it points at the customer. Counting nothing is recoverable;
21
+ * publishing a false accusation about somebody's product is not.
28
22
  *
29
- * Ordering matters for the false-green flag: a contradiction found alongside a PASSING assertion is
30
- * the case the product exists for — the screen looked right, the write failed, and every other tool
31
- * on the market would have called it green. That pairing is what `falseGreen` marks.
23
+ * Bringing it back needs evidence that actually separates the cases. It does not exist in the payload
24
+ * today: `element.present` covers "the button is missing", "the API is down" and "the agent mistyped
25
+ * a testid" identically, and `signal.absent` covers both a signal the app never fired and a ref
26
+ * Reticle drove into nothing. Until a verdict carries the reason it came out that way, the honest
27
+ * output is no owner at all.
32
28
  */
33
29
  export declare function bugsInResult(toolName: string, result: Record<string, unknown>): BugCandidate[];
@@ -15,6 +15,7 @@
15
15
  * `console-error`, `duplicate-request`. Never a selector, a URL, an element, or a description of
16
16
  * anyone's app: that a class of defect was found, never what it was found in.
17
17
  */
18
+ import { ReticleTool } from '../tools/tool-names.js';
18
19
  import { BugSource, ContradictionKind } from '@reticlehq/core';
19
20
  /** Cap per tool call — a pathological crawl must not turn one call into a thousand events. */
20
21
  const MAX_BUGS_PER_CALL = 25;
@@ -24,7 +25,7 @@ function kindsOf(value) {
24
25
  return value
25
26
  .map((entry) => {
26
27
  const kind = entry?.kind;
27
- return typeof kind === 'string' ? kind : undefined;
28
+ return 'string' === typeof kind ? kind : undefined;
28
29
  })
29
30
  .filter((kind) => kind !== undefined);
30
31
  }
@@ -35,16 +36,111 @@ function kindsOf(value) {
35
36
  * the case the product exists for — the screen looked right, the write failed, and every other tool
36
37
  * on the market would have called it green. That pairing is what `falseGreen` marks.
37
38
  */
39
+ /**
40
+ * The verdict this result carries, whatever envelope it arrived in.
41
+ *
42
+ * `reticle_assert` reports a top-level boolean `pass`. `reticle_act_and_wait` — the tool agents
43
+ * actually reach for, and the one whose description calls it the act->observe->assert loop — reports
44
+ * `verdict.pass` with the summary at `verified`. Reading only `result.pass` meant a FAILED
45
+ * act_and_wait produced no bug at all, and a contradiction found next to a PASSING one was recorded
46
+ * with `falseGreen: false` — deflating the single number this product exists to publish, by the
47
+ * shape of its own envelope.
48
+ */
49
+ function verdictOf(result) {
50
+ if ('boolean' === typeof result['pass'])
51
+ return result['pass'];
52
+ const verdict = result['verdict'];
53
+ if ('object' === typeof verdict && verdict !== null) {
54
+ const nested = verdict['pass'];
55
+ if ('boolean' === typeof nested)
56
+ return nested;
57
+ }
58
+ // `verified` is the field the agent is told to gate on; "unknown" is deliberately not a verdict.
59
+ const verified = result['verified'];
60
+ if ('yes' === verified)
61
+ return true;
62
+ if ('no' === verified)
63
+ return false;
64
+ return undefined;
65
+ }
66
+ /** The assertion label, from either envelope — act_and_wait nests it under `verdict`. */
67
+ function assertionOf(result) {
68
+ if (result['assertion'] !== undefined)
69
+ return result['assertion'];
70
+ const verdict = result['verdict'];
71
+ return 'object' === typeof verdict && verdict !== null
72
+ ? verdict['assertion']
73
+ : undefined;
74
+ }
75
+ /**
76
+ * A verdict that could not be reached is not a defect found. `inconclusive` is set when the
77
+ * assertion was never evaluated — an under-specified call, or nothing instrumented to read — so
78
+ * counting it would put the agent's own mistakes into the number we publish.
79
+ */
80
+ function isInconclusive(result) {
81
+ const verdict = result['verdict'];
82
+ return ('object' === typeof verdict &&
83
+ verdict !== null &&
84
+ 'string' === typeof verdict['inconclusive']);
85
+ }
86
+ /** A suite row whose replay never STARTED — see SuiteFlowResult.couldNotRun. */
87
+ function couldNotRun(failure) {
88
+ return ('object' === typeof failure &&
89
+ failure !== null &&
90
+ true === failure.couldNotRun);
91
+ }
92
+ /**
93
+ * `bug.attribution` was REMOVED, deliberately, and this note is here so it is not re-added casually.
94
+ *
95
+ * It shipped twice and was wrong both times. First it stamped `app` on every contradiction, including
96
+ * `request-never-settled` — which Reticle raises from the ABSENCE of a settle event, so a dev-overlay
97
+ * poll manufactured it with the app doing nothing wrong. That was fixed, and a real drive then found
98
+ * it still inverted: across two full runs, EVERY `attribution: 'app'` was a misattribution, while the
99
+ * one defect that genuinely was a bad agent predicate carried none. One session would have published
100
+ * "2 defects in the app" against a true count of 0.
101
+ *
102
+ * A metric that is confidently wrong about WHOSE fault a defect is, is worse than no metric: it is
103
+ * the number a founder steers on, and it points at the customer. Counting nothing is recoverable;
104
+ * publishing a false accusation about somebody's product is not.
105
+ *
106
+ * Bringing it back needs evidence that actually separates the cases. It does not exist in the payload
107
+ * today: `element.present` covers "the button is missing", "the API is down" and "the agent mistyped
108
+ * a testid" identically, and `signal.absent` covers both a signal the app never fired and a ref
109
+ * Reticle drove into nothing. Until a verdict carries the reason it came out that way, the honest
110
+ * output is no owner at all.
111
+ */
38
112
  export function bugsInResult(toolName, result) {
113
+ // `reticle_run` is a WRAPPER: its handler calls runTool on the real tool, which already reported
114
+ // that result's defects under the real tool's name — then the outer chokepoint reports the very
115
+ // same object again as `reticle_run`. One sweep of 34 events was a perfect mirror image:
116
+ // flow-regression x8/reticle_flow_verify AND x8/reticle_run; assertion-failed x8/reticle_verify_change
117
+ // AND x8/reticle_run. Sixteen of the thirty-four were echoes, and a headline number that doubles
118
+ // because of HOW a tool was reached is not a measurement.
119
+ //
120
+ // Nothing is lost by staying silent here: when the inner tool refuses or throws, the wrapper
121
+ // returns { error, tool, params, hint }, which carries no verdict and no failures to classify.
122
+ if (toolName === ReticleTool.RUN)
123
+ return [];
39
124
  const bugs = [];
40
- const passed = result['pass'] === true;
125
+ const verdict = verdictOf(result);
126
+ const passed = true === verdict;
41
127
  // 1. Contradictions — channels disagreeing. Invisible to a human watching the screen.
42
128
  for (const kind of kindsOf(result['contradictions'])) {
43
- bugs.push({ source: BugSource.CONTRADICTION, kind, falseGreen: passed, tool: toolName });
129
+ // A contradiction is two of the APP's own channels disagreeing — never the agent's phrasing.
130
+ // Omitted for the one kind Reticle can produce on its own: see contradictionAttribution.
131
+ bugs.push({
132
+ source: BugSource.CONTRADICTION,
133
+ kind,
134
+ falseGreen: passed,
135
+ tool: toolName,
136
+ });
44
137
  }
45
138
  // 2. Crawl anomalies — found autonomously, with nobody writing a test. Crawl reports its own
46
139
  // contradictions inside `anomalies` too, so classify by kind rather than by which array it sat in.
47
140
  for (const kind of kindsOf(result['anomalies'])) {
141
+ // Crawl reports contradictions in this array too, so the same kind must get the same owner
142
+ // wherever it arrives — a `request-never-settled` found by the crawler is no better evidence
143
+ // about whose fault it was than one found by an assertion.
48
144
  bugs.push({
49
145
  source: BugSource.CRAWL,
50
146
  kind,
@@ -52,28 +148,44 @@ export function bugsInResult(toolName, result) {
52
148
  // — even though no assertion was made. See the definition on `falseGreen`.
53
149
  falseGreen: CONTRADICTION_KINDS.has(kind),
54
150
  tool: toolName,
151
+ // Nobody wrote a predicate here, so there is no bad request to blame: a crawl anomaly is a
152
+ // fault the app produced on its own.
55
153
  });
56
154
  }
57
155
  // 3. A failed assertion — the agent declared a consequence and it did not hold.
58
156
  // Only when nothing above already explained it, or one defect would be counted twice.
59
- if (result['pass'] === false && bugs.length === 0) {
60
- const reason = result['assertion'];
157
+ // A red the APP earned, not one Reticle produced. `verified: "unknown"` is the honesty layer
158
+ // saying nothing was proved either way, and a top-level `error` is a refusal — measured over one
159
+ // sweep, every bug_found emitted was one of these two: "no session connected" from reticle_run and
160
+ // an unverifiable suite from reticle_verify_change, counted as defects in the user's app.
161
+ const reticleFailure = 'unknown' === result['verified'] || 'string' === typeof result['error'];
162
+ if (false === verdict && 0 === bugs.length && !isInconclusive(result) && !reticleFailure) {
163
+ const reason = assertionOf(result);
164
+ const kind = 'string' === typeof reason && reason.length > 0 ? reason.slice(0, 64) : 'assertion-failed';
61
165
  bugs.push({
62
166
  source: BugSource.ASSERTION,
63
- kind: typeof reason === 'string' && reason.length > 0 ? reason.slice(0, 64) : 'assertion-failed',
167
+ kind,
64
168
  falseGreen: false,
65
169
  tool: toolName,
170
+ // Omitted when the label cannot say whose fault it was — see ASSERTION_ATTRIBUTION.
66
171
  });
67
172
  }
68
173
  // 4. Replay failures — a saved flow that used to pass no longer does. That is a regression caught,
69
174
  // which is a different and stronger claim than "a check failed".
70
- if (result['status'] === 'fail' && Array.isArray(result['failures'])) {
71
- for (const _failure of result['failures'].slice(0, MAX_BUGS_PER_CALL)) {
175
+ if ('fail' === result['status'] && Array.isArray(result['failures'])) {
176
+ for (const failure of result['failures'].slice(0, MAX_BUGS_PER_CALL)) {
177
+ // A suite that failed because nothing could RUN is not a regression in the user's app. This
178
+ // rule never looked past `status`, so a project whose flow files did not resolve reported one
179
+ // "defect found" per flow — Reticle's own failure, published as the customer's.
180
+ if (couldNotRun(failure))
181
+ continue;
72
182
  bugs.push({
73
183
  source: BugSource.REPLAY,
74
184
  kind: 'flow-regression',
75
185
  falseGreen: false,
76
186
  tool: toolName,
187
+ // A saved flow that used to pass and no longer does is a regression in the app. Rows that
188
+ // never RAN are skipped above precisely so Reticle's own failures do not land here.
77
189
  });
78
190
  }
79
191
  }
@@ -1,13 +1,3 @@
1
- /**
2
- * Report that a person ran a `reticle` subcommand.
3
- *
4
- * `_daemon` is EXCLUDED, and that exclusion is the whole point of this function. `reticle mcp` and
5
- * `reticle serve` start the daemon by re-running this very binary, so the child re-entered the CLI
6
- * entry point and emitted a second event for what a person experienced as ONE action. The old
7
- * `invoke` metric was therefore inflated ~2x — and inflated worst on the agent-driven sessions that
8
- * matter most, while one-shot commands like `version` counted once. That skewed the RATIO between
9
- * commands, not merely the scale, which is the kind of error you cannot correct for after the fact.
10
- * The daemon\'s own lifecycle is already reported by `daemon_started` / `daemon_stopped`; a spawned
11
- * daemon is not a CLI run.
12
- */
1
+ /** True when this command represents a person typing something. Exported for the gate. */
2
+ export declare function isHumanCliCommand(command: string): boolean;
13
3
  export declare function reportCliRun(argv: readonly string[]): void;