@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
@@ -19,8 +19,20 @@ import { BrowserLaunchKind, } from '@reticlehq/core';
19
19
  import { errorSkeleton, fingerprintError } from './error-fingerprint.js';
20
20
  import { describeToolParams } from './argument-shape.js';
21
21
  import { machineSnapshot } from './machine-snapshot.js';
22
+ import { classifyError } from './error-class.js';
22
23
  /** Cap the distinct error shapes held in memory — a pathological loop must not grow unbounded. */
23
24
  const MAX_ERROR_KINDS = 40;
25
+ /** Distinct tool names whose repeat-runs are tracked. Its own cap: the tool surface, not error shapes. */
26
+ const MAX_REPEAT_TOOLS = 64;
27
+ /**
28
+ * Tool errors that all mean the same thing to a user: the agent asked, and there was no app it could
29
+ * reach. Three different messages, one experience — "nothing is connected" — and it is the error
30
+ * that ends most sessions before a single verification happens.
31
+ *
32
+ * `multiple sessions connected` belongs here too: the agent could not reach AN app because it could
33
+ * not tell which one, and the outcome is identical.
34
+ */
35
+ const NO_SESSION_ERROR = /no browser session connected|no connected session with id|multiple sessions connected/i;
24
36
  /** Cap distinct MCP clients recorded; more than a handful on one daemon is already the story. */
25
37
  const MAX_CLIENTS = 8;
26
38
  /** Bounds on the parameter map. Our own schemas are already finite; these are a belt-and-braces cap. */
@@ -35,6 +47,13 @@ const MAX_PARAMS_PER_TOOL = 24;
35
47
  * strings and costs nothing to hold.
36
48
  */
37
49
  const BREADCRUMB_LENGTH = 12;
50
+ /**
51
+ * Actions driven with no verdict before Reticle asks for one.
52
+ *
53
+ * Three, because the median verdict-less session made FOUR tool calls (2026-08-10/11). A higher
54
+ * threshold never fires for the sessions that need it most.
55
+ */
56
+ const UNVERIFIED_ACTION_NUDGE_AT = 3;
38
57
  /**
39
58
  * Mutable counters for one daemon lifetime. A plain class, not a store: it is process-local, it never
40
59
  * persists, and it dies with the daemon.
@@ -43,6 +62,27 @@ export class SessionMetrics {
43
62
  #toolCalls = 0;
44
63
  #toolErrors = 0;
45
64
  #verifications = 0;
65
+ /**
66
+ * Session-LIFETIME totals for the four numbers the funnel is computed from.
67
+ *
68
+ * `reset()` zeroes every window counter on each periodic flush, but `durationMs` is measured from
69
+ * `#startedAt` and is never reset. So the FINAL summary — the one whose docstring promises "the
70
+ * whole session in a single event" — reported a 30-minute session containing zero tool calls,
71
+ * because it only ever carried the residue since the last flush.
72
+ *
73
+ * Measured 2026-08-10/11: **496 of 499 `daemon_stopped` rows had `toolCalls: 0`**, at a median
74
+ * duration of 30.5 minutes, while `session_progress` for the same daemons carried real tool
75
+ * histograms. Anyone computing "did this session use Reticle" off the event named for the end of
76
+ * the session got `no` essentially always. That is not a small skew — it is the funnel reading
77
+ * zero at the exact step this release exists to raise.
78
+ *
79
+ * Window semantics are unchanged: `session_progress` still reports its own window, so nothing that
80
+ * already sums those events double-counts. Only `final: true` swaps in the lifetime numbers.
81
+ */
82
+ #lifetimeToolCalls = 0;
83
+ #lifetimeToolErrors = 0;
84
+ #lifetimeVerifications = 0;
85
+ #lifetimeBugsFound = 0;
46
86
  #toolCounts = new Map();
47
87
  #toolParams = new Map();
48
88
  /** Ring of the most recent tool NAMES — the agent's approach run, for a crash report. */
@@ -58,6 +98,12 @@ export class SessionMetrics {
58
98
  #concurrent = 0;
59
99
  #peakConcurrent = 0;
60
100
  #unknownToolCalls = 0;
101
+ /**
102
+ * Names the agent guessed at, session-LIFETIME — a guess made early must still be reported at
103
+ * the end, so this is deliberately not cleared by a flush.
104
+ */
105
+ #unknownTools = new Map();
106
+ #lifetimeUnknownToolCalls = 0;
61
107
  #bugsFound = 0;
62
108
  #browserMs = 0;
63
109
  #browserCommands = 0;
@@ -71,7 +117,52 @@ export class SessionMetrics {
71
117
  * capped: the kind vocabulary is a bounded enum plus oracle names, not user data.
72
118
  */
73
119
  #seenBugKinds = new Set();
120
+ /**
121
+ * Tool errors meaning "the agent could not reach an app at all" — the single biggest drop-off in
122
+ * the funnel, and until now reachable only by unpacking `errors[]` in HogQL. 74% of daemons never
123
+ * call a tool, and of the sessions that made exactly one call, most bounced on this.
124
+ */
125
+ #noSessionErrors = 0;
126
+ /** Longest back-to-back run per tool — a retry loop, which `toolCounts` cannot distinguish. */
127
+ #repeatRuns = new Map();
128
+ #lastTool;
129
+ #currentRun = 0;
130
+ /**
131
+ * Actions driven since the last verdict — the run that is left hanging if the loop stops here.
132
+ *
133
+ * NOT `actions - verifications`. That difference ignores ORDER, so a verdict that drove nothing
134
+ * (a `flow_verify` over saved flows) silently paid for a genuinely abandoned action elsewhere, and
135
+ * a session could verify first and act after and look settled. What is abandoned is the trailing
136
+ * run, which is exactly what the field claims to mean.
137
+ */
138
+ #unsettledActions = 0;
139
+ /** One-shot latch for the verdict nudge — see takeUnverifiedNudge. */
140
+ #nudgedUnverified = false;
141
+ /** The agent detached. The one part of `endReason` a query cannot derive. */
142
+ #clientLeft = false;
143
+ /** Feedback invitations shown, session-lifetime — the denominator for feedback_submitted. */
144
+ #feedbackPrompted = 0;
145
+ /** Error buckets by whose defect. Session-lifetime: the mix is a fact about the session. */
146
+ #errorClasses = new Map();
147
+ /**
148
+ * The recovery hinge. A call's outcome is only known once the NEXT one starts, so the verdict for
149
+ * call N-1 is resolved at the start of call N (and for the last call, at summarize).
150
+ */
151
+ #currentCallErrored = false;
152
+ #priorCallErrored = false;
153
+ #errorsRecovered = 0;
154
+ #errorsRepeated = 0;
155
+ /**
156
+ * App SDK connections, session-lifetime. NOT reset by a flush: "did an app ever connect" is a
157
+ * fact about the session, and a page reload after a flush must not erase it.
158
+ */
159
+ #appConnects = 0;
160
+ #firstAppAt;
74
161
  #clients = new Set();
162
+ /** Client name -> version, session-lifetime. Never cleared: who drove is a fact about the session. */
163
+ #clientVersions = new Map();
164
+ /** Which tool surface the daemon advertised. */
165
+ #surface;
75
166
  #startedAt;
76
167
  #now;
77
168
  /** Clock injected — this file must stay testable without a real one, per the repo's clock rule. */
@@ -116,11 +207,48 @@ export class SessionMetrics {
116
207
  this.#browserCommands += 1;
117
208
  }
118
209
  /** A call for a tool that does not exist. Non-zero means our surface is confusing the agent. */
119
- recordUnknownTool() {
210
+ /**
211
+ * An agent reached for a tool that does not exist — and WHAT it reached for.
212
+ *
213
+ * The name is the point. A count says the surface confused someone; the name says which
214
+ * capability they expected us to have, in their own vocabulary. Safe to keep: it is a name from
215
+ * our own namespace, never app data. Bounded and truncated, because a guess is a short
216
+ * identifier and anything longer is not one.
217
+ */
218
+ recordUnknownTool(name) {
120
219
  this.#unknownToolCalls += 1;
220
+ this.#lifetimeUnknownToolCalls += 1;
221
+ if (name === undefined || 0 === name.length)
222
+ return;
223
+ const key = name.slice(0, 64);
224
+ const seen = this.#unknownTools.get(key);
225
+ if (seen !== undefined) {
226
+ this.#unknownTools.set(key, seen + 1);
227
+ return;
228
+ }
229
+ // Stop growing once full: a pathological loop must not balloon the payload. The head is what
230
+ // gets built; the tail is noise.
231
+ if (this.#unknownTools.size >= MAX_ERROR_KINDS)
232
+ return;
233
+ this.#unknownTools.set(key, 1);
121
234
  }
122
235
  recordToolCall(tool, args) {
236
+ // `recordToolCall` runs BEFORE a handler and `recordToolError` after it fails, so a call's
237
+ // outcome is only known once the NEXT call starts. Resolve the one before it here; the final
238
+ // call is resolved in summarize.
239
+ this.#resolveRecovery();
240
+ // A run of the same tool back to back. Five calls to reticle_act is engagement; five in a row
241
+ // after four failures is an agent stuck, and toolCounts reports both as "5".
242
+ this.#currentRun = tool === this.#lastTool ? this.#currentRun + 1 : 1;
243
+ this.#lastTool = tool;
244
+ if (this.#currentRun > 1) {
245
+ const best = this.#repeatRuns.get(tool) ?? 0;
246
+ if (this.#currentRun > best &&
247
+ (this.#repeatRuns.has(tool) || this.#repeatRuns.size < MAX_REPEAT_TOOLS))
248
+ this.#repeatRuns.set(tool, this.#currentRun);
249
+ }
123
250
  this.#toolCalls += 1;
251
+ this.#lifetimeToolCalls += 1;
124
252
  bump(this.#toolCounts, tool);
125
253
  this.#inFlight = tool;
126
254
  this.#breadcrumb.push(tool);
@@ -150,6 +278,13 @@ export class SessionMetrics {
150
278
  */
151
279
  recordToolError(message, tool) {
152
280
  this.#toolErrors += 1;
281
+ this.#lifetimeToolErrors += 1;
282
+ // Whose defect. Only the `schema` bucket is fixable by writing better descriptions, and it was
283
+ // indistinguishable from the rest inside one `toolErrors` count.
284
+ bump(this.#errorClasses, classifyError(message));
285
+ this.#currentCallErrored = true;
286
+ if (NO_SESSION_ERROR.test(message))
287
+ this.#noSessionErrors += 1;
153
288
  const fingerprint = fingerprintError(message);
154
289
  const existing = this.#errorKinds.get(fingerprint);
155
290
  if (existing !== undefined) {
@@ -194,8 +329,51 @@ export class SessionMetrics {
194
329
  get trail() {
195
330
  return { breadcrumb: [...this.#breadcrumb], inFlight: this.#inFlight };
196
331
  }
332
+ /**
333
+ * Ask the agent for a verdict, once, when it has driven the page without asking for one.
334
+ *
335
+ * Measured 2026-08-10/11: of 170 sessions that made a tool call, 140 produced no verdict — and
336
+ * **137 of those never called a verdict-producing tool even once.** They drove the app 269 times
337
+ * with `reticle_act` and never asked whether it worked. This counter already existed
338
+ * (`abandonedActions`, non-zero in 58 sessions); it was reported to US and never to the agent.
339
+ *
340
+ * Three is the threshold because the median verdict-less session made four tool calls: any higher
341
+ * and it never fires for the sessions that need it. Re-arms after a verdict, so a second
342
+ * abandoned run is caught — the loop can break more than once in a long session.
343
+ */
344
+ takeUnverifiedNudge() {
345
+ if (this.#unsettledActions < UNVERIFIED_ACTION_NUDGE_AT || this.#nudgedUnverified) {
346
+ return undefined;
347
+ }
348
+ this.#nudgedUnverified = true;
349
+ return (`you have driven this page ${String(this.#unsettledActions)} times without asking for a ` +
350
+ 'verdict. Only reticle_act_and_wait and reticle_assert produce one — everything else moves ' +
351
+ 'or reads the app and proves nothing. Name the consequence you expect and check it: ' +
352
+ 'reticle_assert({ sessionId, since, predicate }), or use reticle_act_and_wait({ ref, ' +
353
+ 'action, until }) for the next step so the action and its check are one call.');
354
+ }
355
+ /**
356
+ * An app's SDK dialled this daemon.
357
+ *
358
+ * Zero of these at shutdown means the daemon ran and no app ever connected — a broken install.
359
+ * Non-zero with no tool calls is the opposite problem. See SessionSummary.appConnects.
360
+ */
361
+ recordAppConnected() {
362
+ this.#appConnects += 1;
363
+ this.#firstAppAt ??= this.#now();
364
+ }
365
+ /** One action driven at the page. Settled by the next verdict; see #unsettledActions. */
366
+ recordAction() {
367
+ this.#unsettledActions += 1;
368
+ }
197
369
  recordVerification() {
198
370
  this.#verifications += 1;
371
+ this.#lifetimeVerifications += 1;
372
+ // A verdict settles whatever was outstanding, however many actions it took to get there.
373
+ this.#unsettledActions = 0;
374
+ // Re-arm the nudge: the loop can break more than once in an 11-hour session, and an agent that
375
+ // complied once and then drifted again is exactly the case worth catching.
376
+ this.#nudgedUnverified = false;
199
377
  }
200
378
  /**
201
379
  * One defect found in the app under test — the outcome number, kept broken down by kind.
@@ -206,6 +384,7 @@ export class SessionMetrics {
206
384
  */
207
385
  recordBug(kind) {
208
386
  this.#bugsFound += 1;
387
+ this.#lifetimeBugsFound += 1;
209
388
  if (this.#bugKinds.size < MAX_ERROR_KINDS || this.#bugKinds.has(kind))
210
389
  bump(this.#bugKinds, kind);
211
390
  const first = !this.#seenBugKinds.has(kind);
@@ -237,27 +416,83 @@ export class SessionMetrics {
237
416
  stats.failures[failure] = (stats.failures[failure] ?? 0) + 1;
238
417
  };
239
418
  }
240
- recordClient(name) {
419
+ recordClient(name, version) {
420
+ const key = name.slice(0, 64);
241
421
  if (this.#clients.size < MAX_CLIENTS)
242
- this.#clients.add(name.slice(0, 64));
422
+ this.#clients.add(key);
423
+ // The version was already in the handshake and was being dropped. A regression in agent
424
+ // behaviour is usually a client version, and without this it is unattributable.
425
+ if (version !== undefined && '' !== version && this.#clientVersions.size < MAX_CLIENTS) {
426
+ this.#clientVersions.set(key, version.slice(0, 32));
427
+ }
428
+ }
429
+ /**
430
+ * The agent's client went away.
431
+ *
432
+ * This is the only part of `endReason` that a query cannot derive from the counters: "the client
433
+ * detached" and "the agent stopped asking" produce identical numbers and are different findings —
434
+ * one is the task ending, the other is us losing them.
435
+ */
436
+ recordClientLeft() {
437
+ this.#clientLeft = true;
438
+ }
439
+ /** How many times in a row the current tool has been called. Read by the friction invite. */
440
+ get currentRun() {
441
+ return this.#currentRun;
442
+ }
443
+ /** Reticle invited the agent to send feedback. The denominator for feedback_submitted. */
444
+ recordFeedbackPrompt() {
445
+ this.#feedbackPrompted += 1;
446
+ }
447
+ /** Which tool surface was advertised to agents this session. */
448
+ recordSurface(surface) {
449
+ this.#surface = surface.slice(0, 32);
243
450
  }
244
451
  /**
245
452
  * Roll the counters into the event payload. `final` marks a clean shutdown; a periodic flush passes
246
453
  * false so a session killed before it can exit is not lost entirely (see the flush note in cli.ts).
247
454
  */
248
- summarize(final) {
455
+ summarize(final, exit) {
456
+ // The last call has no successor to resolve it, so resolve it here. Only on a FINAL summary:
457
+ // a periodic flush is not the end of anything and would resolve a call still in flight.
458
+ if (final)
459
+ this.#resolveRecovery();
249
460
  const machine = machineSnapshot();
461
+ // A FINAL summary reports the session; a periodic flush reports its window. `durationMs` was
462
+ // always session-lifetime, so a windowed count next to it described a 30-minute session that
463
+ // made no calls. The histograms below stay windowed by design — summing them across a session's
464
+ // events is correct, and duplicating every map to make them lifetime would double the memory
465
+ // this class exists to keep small.
466
+ const scalars = final
467
+ ? {
468
+ toolCalls: this.#lifetimeToolCalls,
469
+ toolErrors: this.#lifetimeToolErrors,
470
+ verifications: this.#lifetimeVerifications,
471
+ bugsFound: this.#lifetimeBugsFound,
472
+ }
473
+ : {
474
+ toolCalls: this.#toolCalls,
475
+ toolErrors: this.#toolErrors,
476
+ verifications: this.#verifications,
477
+ bugsFound: this.#bugsFound,
478
+ };
250
479
  return {
251
480
  durationMs: Math.max(0, this.#now() - this.#startedAt),
252
- toolCalls: this.#toolCalls,
481
+ toolCalls: scalars.toolCalls,
253
482
  toolCounts: Object.fromEntries(this.#toolCounts),
254
- toolErrors: this.#toolErrors,
483
+ toolErrors: scalars.toolErrors,
255
484
  ...(this.#errorKinds.size > 0 ? { errors: [...this.#errorKinds.values()] } : {}),
256
485
  sdkFailures: this.#sdkFailures,
257
486
  ...(this.#sdkFailureKinds.size > 0 ? { sdkErrors: [...this.#sdkFailureKinds.values()] } : {}),
258
- verifications: this.#verifications,
259
- bugsFound: this.#bugsFound,
487
+ verifications: scalars.verifications,
488
+ bugsFound: scalars.bugsFound,
260
489
  ...(this.#bugKinds.size > 0 ? { bugKinds: Object.fromEntries(this.#bugKinds) } : {}),
490
+ ...(this.#noSessionErrors > 0 ? { noSessionErrors: this.#noSessionErrors } : {}),
491
+ ...(this.#repeatRuns.size > 0
492
+ ? { consecutiveRepeats: Object.fromEntries(this.#repeatRuns) }
493
+ : {}),
494
+ // Absent rather than zero, so the PRESENCE of the field is the signal on a dashboard.
495
+ ...(this.#unsettledActions > 0 ? { abandonedActions: this.#unsettledActions } : {}),
261
496
  ...(this.#toolParams.size > 0
262
497
  ? {
263
498
  toolParams: Object.fromEntries([...this.#toolParams].map(([tool, params]) => [tool, Object.fromEntries(params)])),
@@ -269,25 +504,87 @@ export class SessionMetrics {
269
504
  browserMs: this.#browserMs,
270
505
  browserCommands: this.#browserCommands,
271
506
  peakConcurrentTools: this.#peakConcurrent,
272
- unknownToolCalls: this.#unknownToolCalls,
507
+ unknownToolCalls: final ? this.#lifetimeUnknownToolCalls : this.#unknownToolCalls,
508
+ ...(this.#unknownTools.size > 0
509
+ ? { unknownTools: Object.fromEntries(this.#unknownTools) }
510
+ : {}),
273
511
  ...(machine !== undefined ? { machine } : {}),
274
512
  ...(this.#clients.size > 0 ? { clients: [...this.#clients] } : {}),
513
+ ...(this.#clientVersions.size > 0
514
+ ? { clientVersions: Object.fromEntries(this.#clientVersions) }
515
+ : {}),
516
+ ...(this.#surface === undefined ? {} : { surface: this.#surface }),
517
+ ...(final ? { endReason: this.#endReason() } : {}),
518
+ ...(this.#feedbackPrompted > 0 ? { feedbackPrompted: this.#feedbackPrompted } : {}),
519
+ ...(this.#errorClasses.size > 0
520
+ ? { errorClasses: Object.fromEntries(this.#errorClasses) }
521
+ : {}),
522
+ ...(this.#errorsRecovered > 0 ? { errorsRecovered: this.#errorsRecovered } : {}),
523
+ ...(this.#errorsRepeated > 0 ? { errorsRepeated: this.#errorsRepeated } : {}),
524
+ // Always present, including zero: absence and "no app ever connected" must not look alike,
525
+ // because zero IS the finding here.
526
+ appConnects: this.#appConnects,
527
+ ...(this.#firstAppAt === undefined
528
+ ? {}
529
+ : { msToFirstApp: Math.max(0, this.#firstAppAt - this.#startedAt) }),
275
530
  final,
531
+ // Only on a real exit. A periodic flush carrying `exit: "unknown"` would read as a daemon that
532
+ // died without a shutdown path, which is the one thing this field exists to make visible.
533
+ ...(final && exit !== undefined ? { exit } : {}),
276
534
  };
277
535
  }
536
+ /**
537
+ * Decide whether the error before the previous call was recovered from or repeated.
538
+ *
539
+ * Called at the start of each new call (resolving the one before it) and once at summarize (for
540
+ * the last call). Shifting rather than counting on the spot is what makes "the agent tried
541
+ * something else and it worked" distinguishable from "it failed the same way again" — an earlier
542
+ * version counted a recovery the moment a call STARTED, so a call that then failed was recorded
543
+ * as a success.
544
+ */
545
+ #resolveRecovery() {
546
+ if (this.#priorCallErrored) {
547
+ if (this.#currentCallErrored)
548
+ this.#errorsRepeated += 1;
549
+ else
550
+ this.#errorsRecovered += 1;
551
+ }
552
+ this.#priorCallErrored = this.#currentCallErrored;
553
+ this.#currentCallErrored = false;
554
+ }
555
+ /**
556
+ * What state the agent's work was in at the end. Ordered by which fact outranks which: a client
557
+ * that left explains everything after it, and a verdict settles whatever preceded it.
558
+ */
559
+ #endReason() {
560
+ if (0 === this.#lifetimeToolCalls)
561
+ return 'never_used';
562
+ if (this.#clientLeft)
563
+ return 'client_left';
564
+ if (this.#unsettledActions > 0)
565
+ return 'abandoned';
566
+ if (this.#lifetimeVerifications > 0)
567
+ return 'verified';
568
+ return 'explored';
569
+ }
278
570
  /** True when nothing at all happened — a flush of an idle daemon is not worth an event. */
279
571
  get empty() {
280
- return (this.#toolCalls === 0 &&
281
- this.#verifications === 0 &&
282
- this.#toolErrors === 0 &&
283
- this.#bugsFound === 0 &&
284
- this.#sdkFailures === 0);
572
+ return (0 === this.#toolCalls &&
573
+ 0 === this.#verifications &&
574
+ 0 === this.#toolErrors &&
575
+ 0 === this.#bugsFound &&
576
+ 0 === this.#sdkFailures);
285
577
  }
286
578
  /** Zero the counters after a non-final flush so the next flush reports the NEXT window, not a total. */
287
579
  reset() {
288
580
  this.#toolCalls = 0;
289
581
  this.#toolErrors = 0;
290
582
  this.#verifications = 0;
583
+ this.#noSessionErrors = 0;
584
+ this.#repeatRuns.clear();
585
+ this.#lastTool = undefined;
586
+ this.#currentRun = 0;
587
+ this.#unsettledActions = 0;
291
588
  this.#toolCounts.clear();
292
589
  this.#toolParams.clear();
293
590
  this.#errorKinds.clear();
@@ -302,7 +599,10 @@ export class SessionMetrics {
302
599
  this.#unknownToolCalls = 0;
303
600
  this.#bugsFound = 0;
304
601
  this.#bugKinds.clear();
305
- this.#seenBugKinds.clear();
602
+ // #seenBugKinds is deliberately NOT cleared. It is not a window counter — it is the
603
+ // session-lifetime memory behind `repeat` on bug_found, and zeroing it made the same defect,
604
+ // found again after a flush, report as a newly distinct one. Sessions run to 11.5 hours in the
605
+ // data, so that was up to 23 chances to count one defect many times.
306
606
  }
307
607
  }
308
608
  function bump(map, key) {
@@ -1,4 +1,38 @@
1
- import { TelemetryEventKind, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type McpConnection, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core';
1
+ import { TelemetryEventKind, ProjectIdSource, type Crash, type Feedback, type Identity, type BugFound, type InitOutcome, type McpConnection, type McpOutage, type ProjectProfile, type SessionSummary, type TelemetryActor, type Verification, type VersionChange } from '@reticlehq/core';
2
+ /**
3
+ * Feedback gets its own, much larger budget — and a retry.
4
+ *
5
+ * 2s + drop is right for a usage counter: the metric is worthless if it costs the user latency, and
6
+ * losing one changes nothing. Feedback is the opposite on both counts. It is the only qualitative
7
+ * channel the product has, it carries an agent's entire root-cause analysis, and losing one loses
8
+ * work that cannot be reconstructed.
9
+ *
10
+ * Measured to the collector with a WARM DNS cache: dns 0.003s, connect 0.233s, tls 0.467s, total
11
+ * 0.694s — a third of the old budget gone before any payload moves, on the good path. A fresh
12
+ * short-lived CLI process pays cold DNS and a cold route on top, which is exactly the case that
13
+ * failed in the field and then succeeded on retry from a warmer process.
14
+ */
15
+ export declare const FEEDBACK_TIMEOUT_MS = 15000;
16
+ export declare const FEEDBACK_RETRIES = 1;
17
+ export declare const FEEDBACK_RETRY_BACKOFF_MS = 750;
18
+ /**
19
+ * One-way project fingerprint: distinct-count of this = "projects Reticle is used in", reveals nothing.
20
+ *
21
+ * Prefers a hash of the git ORIGIN over a hash of the cwd. The contract in `@reticlehq/core` always
22
+ * said "one-way hash of the git remote (or cwd)", but the implementation only ever hashed the path —
23
+ * which meant the same repo cloned by four teammates, or checked out twice on one machine, counted as
24
+ * four separate projects. That inflates the project count and, worse, makes team adoption look like
25
+ * unrelated individuals. Hashing the origin dedupes a real project down to one id across every clone.
26
+ *
27
+ * Still one-way, still non-reversible, and still exactly what the published policy describes: the
28
+ * remote URL itself never leaves the machine, only its SHA-256. The URL is normalized first (scheme,
29
+ * credentials, `.git` suffix and case removed) so `git@github.com:acme/web.git` and
30
+ * `https://github.com/Acme/web` are recognized as the same project rather than two.
31
+ */
32
+ export declare const projectFingerprint: (cwd: string) => {
33
+ projectId: string;
34
+ source: ProjectIdSource;
35
+ };
2
36
  /** The current telemetry state and which control set it — what `reticle telemetry status` prints. */
3
37
  export declare const describeTelemetry: (env?: NodeJS.ProcessEnv, dir?: string, cwd?: string) => {
4
38
  enabled: boolean;
@@ -38,6 +72,8 @@ export interface TelemetryExtra {
38
72
  init?: InitOutcome;
39
73
  /** `bug_found`: one defect Reticle found in the app under test. */
40
74
  bug?: BugFound;
75
+ /** `mcp_connection_lost`: the agent lost its tools — which stage, and why. */
76
+ outage?: McpOutage;
41
77
  }
42
78
  export interface Telemetry {
43
79
  /**
@@ -45,7 +81,14 @@ export interface Telemetry {
45
81
  * CLI process can exit immediately instead of waiting out the POST (use it from command entry
46
82
  * points; daemon-side events send in-process).
47
83
  */
48
- emit(kind: TelemetryEventKind, extra?: TelemetryExtra): Promise<void>;
84
+ /**
85
+ * Send one event. Resolves to whether it was DELIVERED.
86
+ *
87
+ * Almost every caller ignores the result and should — a lost metric must never surface to a user.
88
+ * `reticle_feedback` is the exception: it hands a receipt to an agent, and reporting "filed" for a
89
+ * send that failed loses the report and lies about it in the same breath.
90
+ */
91
+ emit(kind: TelemetryEventKind, extra?: TelemetryExtra): Promise<boolean>;
49
92
  readonly enabled: boolean;
50
93
  /** True the very first run on this machine — the CLI emits INSTALL alongside the first INVOKE. */
51
94
  readonly firstRun: boolean;