@reticlehq/server 3.5.0 → 3.7.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 (721) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +123 -116
  3. package/dist/chromium-launch-options.d.ts +3 -1
  4. package/dist/chromium-launch-options.js +2 -1
  5. package/dist/command/cli/adhoc-suite.d.ts +14 -0
  6. package/dist/command/cli/adhoc-suite.js +42 -7
  7. package/dist/command/cli/adhoc-verdict.d.ts +19 -2
  8. package/dist/command/cli/adhoc-verdict.js +76 -9
  9. package/dist/command/cli/answers/gate-exit.d.ts +23 -0
  10. package/dist/command/cli/answers/gate-exit.js +45 -0
  11. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  12. package/dist/command/cli/answers/gate-hook-message.js +3 -1
  13. package/dist/command/cli/answers/open-note.d.ts +2 -0
  14. package/dist/command/cli/answers/open-note.js +22 -0
  15. package/dist/command/cli/auth/device-flow.d.ts +13 -2
  16. package/dist/command/cli/auth/device-flow.js +17 -0
  17. package/dist/command/cli/cli-doctor.js +14 -0
  18. package/dist/command/cli/cli-flow-commands.d.ts +0 -11
  19. package/dist/command/cli/cli-flow-commands.js +31 -112
  20. package/dist/command/cli/cli-kill.js +3 -2
  21. package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
  22. package/dist/command/cli/cli-parse-grammar.js +75 -0
  23. package/dist/command/cli/cli-parse-verify.d.ts +2 -0
  24. package/dist/command/cli/cli-parse-verify.js +18 -0
  25. package/dist/command/cli/cli-parse.d.ts +19 -28
  26. package/dist/command/cli/cli-parse.js +82 -182
  27. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  28. package/dist/command/cli/cli-update-commands.js +61 -9
  29. package/dist/command/cli/cli-usage.d.ts +33 -0
  30. package/dist/command/cli/cli-usage.js +287 -0
  31. package/dist/command/cli/cli-verify.d.ts +35 -5
  32. package/dist/command/cli/cli-verify.js +114 -35
  33. package/dist/command/cli/cloud-cli.d.ts +13 -0
  34. package/dist/command/cli/cloud-cli.js +192 -341
  35. package/dist/command/cli/cloud-kit.d.ts +12 -0
  36. package/dist/command/cli/cloud-kit.js +41 -2
  37. package/dist/command/cli/cloud-link.d.ts +57 -0
  38. package/dist/command/cli/cloud-link.js +308 -0
  39. package/dist/command/cli/cloud-login.d.ts +22 -0
  40. package/dist/command/cli/cloud-login.js +96 -8
  41. package/dist/command/cli/context-command.d.ts +1 -0
  42. package/dist/command/cli/context-command.js +33 -0
  43. package/dist/command/cli/daemon-start-options.d.ts +3 -0
  44. package/dist/command/cli/daemon-start-options.js +24 -3
  45. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +46 -0
  46. package/dist/command/cli/doctor/browser/chromium-hint.js +99 -1
  47. package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
  48. package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
  49. package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
  50. package/dist/command/cli/doctor/doctor-project-line.js +5 -4
  51. package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
  52. package/dist/command/cli/doctor/doctor-rows.js +6 -0
  53. package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
  54. package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
  55. package/dist/command/cli/drive/drive-attach.d.ts +27 -2
  56. package/dist/command/cli/drive/drive-attach.js +71 -4
  57. package/dist/command/cli/drive/drive-command.d.ts +2 -1
  58. package/dist/command/cli/drive/drive-command.js +13 -7
  59. package/dist/command/cli/known-regression.d.ts +13 -0
  60. package/dist/command/cli/known-regression.js +34 -0
  61. package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
  62. package/dist/command/cli/launch/sdk-version-match.js +25 -0
  63. package/dist/command/cli/lifecycle/daemon-lifecycle.js +15 -3
  64. package/dist/command/cli/mcp-command.d.ts +6 -0
  65. package/dist/command/cli/mcp-command.js +13 -5
  66. package/dist/command/cli/ports/port-holder.d.ts +10 -0
  67. package/dist/command/cli/ports/port-holder.js +25 -1
  68. package/dist/command/cli/project-name.d.ts +29 -1
  69. package/dist/command/cli/project-name.js +107 -2
  70. package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
  71. package/dist/command/cli/setup-mcp-cli.js +35 -12
  72. package/dist/command/cli/status/status-lines.d.ts +1 -0
  73. package/dist/command/cli/status/status-lines.js +47 -2
  74. package/dist/command/cli/status/workspace-links.d.ts +10 -0
  75. package/dist/command/cli/status/workspace-links.js +29 -0
  76. package/dist/command/cli/try-command.d.ts +96 -0
  77. package/dist/command/cli/try-command.js +362 -0
  78. package/dist/command/cli/tutorial.d.ts +0 -100
  79. package/dist/command/cli/tutorial.js +0 -181
  80. package/dist/command/cli/verify-results.d.ts +19 -0
  81. package/dist/command/cli/verify-results.js +29 -0
  82. package/dist/command/cli-onboarding.d.ts +0 -11
  83. package/dist/command/cli-onboarding.js +0 -15
  84. package/dist/command/cli.d.ts +20 -0
  85. package/dist/command/cli.js +130 -104
  86. package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
  87. package/dist/command/daemon/binding/daemon-status-probe.js +1 -0
  88. package/dist/command/daemon/daemon-resilience.js +4 -4
  89. package/dist/command/daemon/daemon-resolve.d.ts +23 -1
  90. package/dist/command/daemon/daemon-resolve.js +79 -9
  91. package/dist/command/setup/agent-writer.d.ts +1 -1
  92. package/dist/command/setup/agent-writer.js +3 -2
  93. package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
  94. package/dist/command/setup/bringup/bridge-port.js +13 -3
  95. package/dist/command/setup/bringup/connect-lease.d.ts +1 -0
  96. package/dist/command/setup/bringup/connect-lease.js +1 -0
  97. package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
  98. package/dist/command/setup/bringup/dev-server-wait.d.ts +8 -0
  99. package/dist/command/setup/bringup/dev-server-wait.js +19 -0
  100. package/dist/command/setup/bringup/ensure-daemon.js +9 -2
  101. package/dist/command/setup/init/agent-restart.d.ts +23 -0
  102. package/dist/command/setup/init/agent-restart.js +28 -0
  103. package/dist/command/setup/init/init-port.d.ts +14 -1
  104. package/dist/command/setup/init/init-port.js +32 -2
  105. package/dist/command/setup/init/init-runtime.d.ts +22 -2
  106. package/dist/command/setup/init/init-runtime.js +114 -38
  107. package/dist/command/setup/node-effects.d.ts +2 -0
  108. package/dist/command/setup/node-effects.js +38 -9
  109. package/dist/command/setup/probe/existing-app.d.ts +12 -0
  110. package/dist/command/setup/probe/existing-app.js +14 -0
  111. package/dist/command/setup/remaining-steps.js +2 -1
  112. package/dist/command/setup/run-setup.d.ts +34 -9
  113. package/dist/command/setup/run-setup.js +65 -51
  114. package/dist/command/setup/setup-command.d.ts +25 -1
  115. package/dist/command/setup/setup-command.js +59 -4
  116. package/dist/command/setup/setup-mcp.js +25 -2
  117. package/dist/command/setup/tauri-signing.d.ts +19 -0
  118. package/dist/command/setup/tauri-signing.js +79 -0
  119. package/dist/command/setup/terminal/listeners.d.ts +10 -1
  120. package/dist/command/setup/terminal/listeners.js +37 -6
  121. package/dist/command/update/update-nudge.d.ts +9 -0
  122. package/dist/command/update/update-nudge.js +1 -1
  123. package/dist/command/update/updater.js +11 -15
  124. package/dist/command/version/version-nudge.js +3 -0
  125. package/dist/command/version/version-skew.d.ts +20 -1
  126. package/dist/command/version/version-skew.js +49 -4
  127. package/dist/features/crawl/crawl-tools.js +4 -0
  128. package/dist/features/crawl/crawl.d.ts +6 -0
  129. package/dist/features/crawl/crawl.js +23 -2
  130. package/dist/features/exhaust/ledger.d.ts +21 -14
  131. package/dist/features/exhaust/ledger.js +35 -9
  132. package/dist/features/harness/app-findings.d.ts +2 -0
  133. package/dist/features/harness/app-findings.js +52 -0
  134. package/dist/features/harness/drive-report.d.ts +61 -1
  135. package/dist/features/harness/drive-report.js +394 -26
  136. package/dist/features/harness/drive-runs.d.ts +120 -0
  137. package/dist/features/harness/drive-runs.js +289 -0
  138. package/dist/features/harness/drivers.d.ts +10 -14
  139. package/dist/features/harness/drivers.js +10 -14
  140. package/dist/features/harness/explore/console-pages.d.ts +15 -0
  141. package/dist/features/harness/explore/console-pages.js +146 -0
  142. package/dist/features/harness/explore/disparity.d.ts +19 -0
  143. package/dist/features/harness/explore/disparity.js +71 -0
  144. package/dist/features/harness/explore/fake-app.d.ts +16 -0
  145. package/dist/features/harness/explore/fake-app.js +49 -0
  146. package/dist/features/harness/explore/goal-search.d.ts +26 -0
  147. package/dist/features/harness/explore/goal-search.js +144 -0
  148. package/dist/features/harness/explore/page-tree.d.ts +49 -0
  149. package/dist/features/harness/explore/page-tree.js +148 -0
  150. package/dist/features/harness/explore/site-map.d.ts +34 -0
  151. package/dist/features/harness/explore/site-map.js +150 -0
  152. package/dist/features/harness/fill-values.d.ts +3 -73
  153. package/dist/features/harness/fill-values.js +3 -169
  154. package/dist/features/harness/goals.d.ts +82 -0
  155. package/dist/features/harness/goals.js +330 -0
  156. package/dist/features/harness/harness.d.ts +51 -1
  157. package/dist/features/harness/harness.js +207 -6
  158. package/dist/features/harness/platform/drive-target.d.ts +30 -0
  159. package/dist/features/harness/platform/drive-target.js +81 -0
  160. package/dist/features/harness/platform/local-apps.d.ts +127 -0
  161. package/dist/features/harness/platform/local-apps.js +280 -0
  162. package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
  163. package/dist/features/harness/platform/persona-credentials.js +69 -0
  164. package/dist/features/harness/platform/platform-drives.d.ts +61 -0
  165. package/dist/features/harness/platform/platform-drives.js +114 -0
  166. package/dist/features/harness/platform/refusal.d.ts +52 -0
  167. package/dist/features/harness/platform/refusal.js +71 -0
  168. package/dist/features/harness/platform/remote-drive.d.ts +186 -0
  169. package/dist/features/harness/platform/remote-drive.js +378 -0
  170. package/dist/features/harness/platform/script.d.ts +94 -0
  171. package/dist/features/harness/platform/script.js +260 -0
  172. package/dist/features/harness/platform/server-driver.d.ts +54 -0
  173. package/dist/features/harness/platform/server-driver.js +182 -0
  174. package/dist/features/harness/platform-config.d.ts +41 -1
  175. package/dist/features/harness/platform-config.js +106 -9
  176. package/dist/features/harness/route-guess.d.ts +13 -0
  177. package/dist/features/harness/route-guess.js +49 -0
  178. package/dist/features/harness/script/app-controls.d.ts +5 -0
  179. package/dist/features/harness/script/app-controls.js +40 -0
  180. package/dist/features/harness/script/lane-ids.d.ts +14 -0
  181. package/dist/features/harness/script/lane-ids.js +16 -0
  182. package/dist/features/harness/script/script-run.d.ts +70 -0
  183. package/dist/features/harness/script/script-run.js +606 -0
  184. package/dist/features/license/license.d.ts +2 -2
  185. package/dist/features/visual/step-shots.d.ts +31 -0
  186. package/dist/features/visual/step-shots.js +231 -0
  187. package/dist/features/visual/visual-tools.d.ts +10 -1
  188. package/dist/features/visual/visual-tools.js +62 -17
  189. package/dist/hooks/client-directory.d.ts +18 -0
  190. package/dist/hooks/client-directory.js +57 -0
  191. package/dist/hooks/coding-agents.d.ts +34 -0
  192. package/dist/hooks/coding-agents.js +117 -0
  193. package/dist/hooks/driven-by.d.ts +23 -0
  194. package/dist/hooks/driven-by.js +56 -0
  195. package/dist/hooks/hook-bus.d.ts +4 -0
  196. package/dist/hooks/hook-bus.js +18 -0
  197. package/dist/hooks/hook-commands.js +15 -4
  198. package/dist/hooks/tool-log.d.ts +9 -0
  199. package/dist/hooks/tool-log.js +29 -0
  200. package/dist/index.d.ts +2 -0
  201. package/dist/index.js +80 -243
  202. package/dist/judgement/capsule/capsule-store.d.ts +2 -2
  203. package/dist/judgement/capsule/causal-summary.d.ts +15 -1
  204. package/dist/judgement/capsule/causal-summary.js +33 -0
  205. package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
  206. package/dist/judgement/runs/artifact/build-verification-run.js +25 -3
  207. package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
  208. package/dist/judgement/runs/artifact/gap-summary.js +12 -3
  209. package/dist/judgement/runs/artifact/replay-mapping.d.ts +1 -1
  210. package/dist/judgement/runs/artifact/replay-mapping.js +37 -11
  211. package/dist/judgement/runs/artifact/run-store.d.ts +14 -2
  212. package/dist/judgement/runs/artifact/run-store.js +195 -3
  213. package/dist/judgement/runs/context-tools.js +59 -1
  214. package/dist/judgement/runs/default-run-id.d.ts +3 -0
  215. package/dist/judgement/runs/default-run-id.js +16 -0
  216. package/dist/judgement/runs/drive-run.d.ts +71 -1
  217. package/dist/judgement/runs/drive-run.js +317 -5
  218. package/dist/judgement/runs/repair-prompt.js +1 -1
  219. package/dist/judgement/runs/resume.d.ts +44 -0
  220. package/dist/judgement/runs/resume.js +137 -0
  221. package/dist/judgement/runs/reticle-runner.js +13 -3
  222. package/dist/judgement/runs/runner-port.d.ts +5 -1
  223. package/dist/judgement/runs/runner-port.js +8 -2
  224. package/dist/judgement/runs/verification-sync.d.ts +5 -2
  225. package/dist/judgement/runs/verification-sync.js +40 -18
  226. package/dist/language/flows/change/gate.d.ts +10 -0
  227. package/dist/language/flows/change/gate.js +22 -0
  228. package/dist/language/flows/decision.js +36 -1
  229. package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
  230. package/dist/language/flows/fields/flow-secret-field.js +25 -2
  231. package/dist/language/flows/flow-anchor.d.ts +61 -10
  232. package/dist/language/flows/flow-anchor.js +98 -6
  233. package/dist/language/flows/flow-cross-step.d.ts +15 -0
  234. package/dist/language/flows/flow-cross-step.js +21 -0
  235. package/dist/language/flows/flow-intent.d.ts +1 -0
  236. package/dist/language/flows/flow-journey.d.ts +6 -0
  237. package/dist/language/flows/flow-journey.js +20 -0
  238. package/dist/language/flows/flow-learning.d.ts +4 -0
  239. package/dist/language/flows/flow-learning.js +45 -14
  240. package/dist/language/flows/flow-mutate-tools.js +5 -3
  241. package/dist/language/flows/flow-replay-run.d.ts +23 -9
  242. package/dist/language/flows/flow-replay-run.js +115 -110
  243. package/dist/language/flows/flow-replay-types.d.ts +1 -1
  244. package/dist/language/flows/flow-replay.d.ts +40 -1
  245. package/dist/language/flows/flow-replay.js +110 -22
  246. package/dist/language/flows/flow-result.d.ts +22 -1
  247. package/dist/language/flows/flow-result.js +47 -2
  248. package/dist/language/flows/flow-scope.js +5 -1
  249. package/dist/language/flows/flow-step-runners.d.ts +22 -2
  250. package/dist/language/flows/flow-step-runners.js +247 -47
  251. package/dist/language/flows/flow-store-for-session.d.ts +2 -2
  252. package/dist/language/flows/flow-store-for-session.js +7 -3
  253. package/dist/language/flows/flow-tools.d.ts +10 -0
  254. package/dist/language/flows/flow-tools.js +112 -89
  255. package/dist/language/flows/flows.d.ts +16 -1
  256. package/dist/language/flows/flows.js +74 -12
  257. package/dist/language/flows/heal-run.js +6 -4
  258. package/dist/language/flows/narrow-write.d.ts +7 -0
  259. package/dist/language/flows/narrow-write.js +36 -1
  260. package/dist/language/flows/recording/flake.d.ts +2 -2
  261. package/dist/language/flows/recording/recording-backtrack.js +14 -1
  262. package/dist/language/flows/recording/tape/recordings.d.ts +58 -4
  263. package/dist/language/flows/recording/tape/recordings.js +95 -13
  264. package/dist/language/flows/replay-from-hud.d.ts +20 -0
  265. package/dist/language/flows/replay-from-hud.js +51 -0
  266. package/dist/language/flows/replay.d.ts +9 -5
  267. package/dist/language/flows/replay.js +56 -15
  268. package/dist/language/flows/single-replay.d.ts +7 -0
  269. package/dist/language/flows/single-replay.js +26 -0
  270. package/dist/language/flows/suite/suite-flakes.js +2 -0
  271. package/dist/language/flows/suite-selection.d.ts +2 -15
  272. package/dist/language/flows/suite-selection.js +4 -3
  273. package/dist/language/flows/verify-change-tools.d.ts +1 -0
  274. package/dist/language/flows/verify-change-tools.js +202 -191
  275. package/dist/launch-chromium.d.ts +27 -5
  276. package/dist/launch-chromium.js +38 -8
  277. package/dist/memory/cloud/cloud-config.d.ts +22 -1
  278. package/dist/memory/cloud/cloud-config.js +34 -5
  279. package/dist/memory/cloud/cloud-sync.d.ts +17 -14
  280. package/dist/memory/cloud/cloud-sync.js +15 -4
  281. package/dist/memory/cloud/harness-config.d.ts +75 -1
  282. package/dist/memory/cloud/harness-config.js +115 -24
  283. package/dist/memory/cloud/hud-notices-source.d.ts +21 -0
  284. package/dist/memory/cloud/hud-notices-source.js +128 -0
  285. package/dist/memory/cloud/memory-scope.d.ts +161 -0
  286. package/dist/memory/cloud/memory-scope.js +227 -0
  287. package/dist/memory/cloud/sync-cycle.d.ts +44 -7
  288. package/dist/memory/cloud/sync-cycle.js +129 -15
  289. package/dist/memory/cloud/sync-daemon.d.ts +11 -0
  290. package/dist/memory/cloud/sync-daemon.js +121 -9
  291. package/dist/memory/cloud/sync-disk.d.ts +1 -1
  292. package/dist/memory/cloud/sync-disk.js +31 -10
  293. package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
  294. package/dist/memory/cloud/sync-screenshots.js +105 -0
  295. package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
  296. package/dist/memory/cloud/unsynced-roots.js +166 -0
  297. package/dist/memory/impact/account-watch.d.ts +34 -0
  298. package/dist/memory/impact/account-watch.js +59 -0
  299. package/dist/memory/impact/impact-recorder.d.ts +8 -0
  300. package/dist/memory/impact/impact-recorder.js +12 -2
  301. package/dist/memory/impact/impact-store.d.ts +17 -4
  302. package/dist/memory/impact/impact-store.js +169 -63
  303. package/dist/memory/intent/inline-intent.d.ts +1 -0
  304. package/dist/memory/intent/intent-shard-store.js +16 -4
  305. package/dist/memory/intent/intent-shard.d.ts +14 -2
  306. package/dist/memory/intent/intent-store.d.ts +1 -0
  307. package/dist/memory/intent/intent-tools.js +122 -4
  308. package/dist/memory/intent/open-intents.d.ts +6 -0
  309. package/dist/memory/intent/open-intents.js +10 -1
  310. package/dist/memory/intent/settled-words.d.ts +24 -0
  311. package/dist/memory/intent/settled-words.js +130 -0
  312. package/dist/memory/journal/drive-flow.d.ts +16 -0
  313. package/dist/memory/journal/drive-flow.js +34 -2
  314. package/dist/memory/journal/drive-run-flush.d.ts +2 -0
  315. package/dist/memory/journal/drive-run-flush.js +20 -5
  316. package/dist/memory/journal/envelope.d.ts +8 -8
  317. package/dist/memory/journal/journal-recorder.js +52 -3
  318. package/dist/memory/journal/on-disk/retention.d.ts +8 -0
  319. package/dist/memory/journal/on-disk/retention.js +32 -2
  320. package/dist/memory/journal/on-disk/startup-maintenance.js +3 -1
  321. package/dist/memory/journal/on-disk/workspace-tiers.js +9 -4
  322. package/dist/memory/journal/session-end.d.ts +15 -1
  323. package/dist/memory/journal/session-end.js +42 -9
  324. package/dist/memory/project/artifact-root-resolver.d.ts +26 -1
  325. package/dist/memory/project/artifact-root-resolver.js +125 -16
  326. package/dist/memory/project/artifact-root.d.ts +46 -8
  327. package/dist/memory/project/artifact-root.js +89 -10
  328. package/dist/memory/project/dir/reticle-dir.d.ts +18 -2
  329. package/dist/memory/project/dir/reticle-dir.js +26 -3
  330. package/dist/memory/project/link-directory.d.ts +15 -0
  331. package/dist/memory/project/link-directory.js +44 -0
  332. package/dist/memory/project/project-tools.js +22 -3
  333. package/dist/memory/project/root-claims.d.ts +6 -0
  334. package/dist/memory/project/root-claims.js +39 -0
  335. package/dist/memory/project/session-root.d.ts +26 -1
  336. package/dist/memory/project/session-root.js +49 -5
  337. package/dist/memory/project/sync-status.d.ts +85 -0
  338. package/dist/memory/project/sync-status.js +224 -0
  339. package/dist/memory/recall/memory-tools.js +47 -2
  340. package/dist/memory/recall/platform-memory.d.ts +34 -0
  341. package/dist/memory/recall/platform-memory.js +139 -0
  342. package/dist/memory/recall/project-memory.d.ts +19 -0
  343. package/dist/memory/recall/project-memory.js +80 -13
  344. package/dist/portal/bridge/bridge.d.ts +9 -3
  345. package/dist/portal/bridge/bridge.js +82 -22
  346. package/dist/portal/bridge/harness-request.d.ts +23 -0
  347. package/dist/portal/bridge/harness-request.js +27 -0
  348. package/dist/portal/bridge/pairing-token.d.ts +9 -0
  349. package/dist/portal/bridge/pairing-token.js +17 -0
  350. package/dist/portal/bridge/wrong-path.d.ts +17 -0
  351. package/dist/portal/bridge/wrong-path.js +35 -0
  352. package/dist/portal/input/network-mock-tools.js +11 -2
  353. package/dist/portal/input/real-input.d.ts +83 -10
  354. package/dist/portal/input/real-input.js +218 -30
  355. package/dist/portal/input/viewport-tools.js +3 -3
  356. package/dist/portal/pool/browser-pool.d.ts +70 -9
  357. package/dist/portal/pool/browser-pool.js +190 -37
  358. package/dist/portal/pool/context-permissions.d.ts +33 -0
  359. package/dist/portal/pool/context-permissions.js +33 -0
  360. package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
  361. package/dist/portal/pool/lease-marker-stamp.js +49 -0
  362. package/dist/portal/pool/playwright-launcher.d.ts +35 -0
  363. package/dist/portal/pool/playwright-launcher.js +64 -3
  364. package/dist/portal/pool/pool-contract.d.ts +28 -5
  365. package/dist/portal/session/agent-link.d.ts +8 -0
  366. package/dist/portal/session/agent-link.js +17 -0
  367. package/dist/portal/session/command-payload.d.ts +8 -0
  368. package/dist/portal/session/command-payload.js +24 -0
  369. package/dist/portal/session/daemon-answers.d.ts +3 -3
  370. package/dist/portal/session/daemon-answers.js +4 -2
  371. package/dist/portal/session/dev-server/dev-command.d.ts +21 -0
  372. package/dist/portal/session/dev-server/dev-command.js +87 -12
  373. package/dist/portal/session/facts/departure-note.d.ts +22 -0
  374. package/dist/portal/session/facts/departure-note.js +47 -0
  375. package/dist/portal/session/facts/observed-state.d.ts +16 -18
  376. package/dist/portal/session/facts/observed-state.js +46 -3
  377. package/dist/portal/session/facts/session-replaced.d.ts +5 -0
  378. package/dist/portal/session/facts/session-replaced.js +5 -0
  379. package/dist/portal/session/first-run-wiring.d.ts +42 -0
  380. package/dist/portal/session/first-run-wiring.js +132 -0
  381. package/dist/portal/session/human/live-control.d.ts +9 -2
  382. package/dist/portal/session/human/live-control.js +12 -3
  383. package/dist/portal/session/human/notes-ledger.d.ts +20 -0
  384. package/dist/portal/session/human/notes-ledger.js +55 -0
  385. package/dist/portal/session/human/review-store.d.ts +12 -0
  386. package/dist/portal/session/human/review-store.js +18 -10
  387. package/dist/portal/session/live-control-tools.d.ts +0 -13
  388. package/dist/portal/session/live-control-tools.js +107 -13
  389. package/dist/portal/session/narration-args.d.ts +5 -0
  390. package/dist/portal/session/narration-args.js +17 -0
  391. package/dist/portal/session/no-session-diagnosis.d.ts +8 -1
  392. package/dist/portal/session/no-session-diagnosis.js +46 -9
  393. package/dist/portal/session/no-session-next-action.d.ts +31 -0
  394. package/dist/portal/session/no-session-next-action.js +159 -60
  395. package/dist/portal/session/no-session-watch.d.ts +14 -1
  396. package/dist/portal/session/no-session-watch.js +227 -17
  397. package/dist/portal/session/presence/session-lease.js +1 -1
  398. package/dist/portal/session/presence/session-mirror.js +3 -0
  399. package/dist/portal/session/recorded-gaps.d.ts +97 -0
  400. package/dist/portal/session/recorded-gaps.js +251 -0
  401. package/dist/portal/session/serving-directory.d.ts +27 -0
  402. package/dist/portal/session/serving-directory.js +45 -0
  403. package/dist/portal/session/session-health.d.ts +18 -0
  404. package/dist/portal/session/session-health.js +9 -1
  405. package/dist/portal/session/session-info.d.ts +8 -1
  406. package/dist/portal/session/session-info.js +8 -1
  407. package/dist/portal/session/session-manager.d.ts +39 -0
  408. package/dist/portal/session/session-manager.js +98 -11
  409. package/dist/portal/session/session-reaper.js +4 -1
  410. package/dist/portal/session/session-successor.d.ts +13 -0
  411. package/dist/portal/session/session-successor.js +1 -1
  412. package/dist/portal/session/session-tools.js +26 -3
  413. package/dist/portal/session/session.d.ts +22 -30
  414. package/dist/portal/session/session.js +81 -79
  415. package/dist/portal/session/timing/command-timeout.js +25 -1
  416. package/dist/portal/session/timing/wall-clock.d.ts +1 -1
  417. package/dist/portal/session/timing/wall-clock.js +1 -1
  418. package/dist/portal/session/zero-install-first-call.d.ts +31 -0
  419. package/dist/portal/session/zero-install-first-call.js +88 -0
  420. package/dist/start-options.d.ts +12 -0
  421. package/dist/start-wiring.d.ts +63 -0
  422. package/dist/start-wiring.js +169 -0
  423. package/dist/status-payload.d.ts +23 -1
  424. package/dist/status-payload.js +36 -1
  425. package/dist/surface/http-server.js +18 -2
  426. package/dist/surface/mcp/attach-memory.d.ts +1 -0
  427. package/dist/surface/mcp/attach-memory.js +12 -1
  428. package/dist/surface/mcp/mcp-proxy.d.ts +9 -0
  429. package/dist/surface/mcp/mcp-proxy.js +65 -13
  430. package/dist/surface/mcp/mcp.d.ts +34 -6
  431. package/dist/surface/mcp/mcp.js +61 -17
  432. package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
  433. package/dist/surface/mcp/proxy/drive-calls.js +27 -0
  434. package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
  435. package/dist/surface/mcp/server-instructions.d.ts +4 -2
  436. package/dist/surface/mcp/server-instructions.js +32 -14
  437. package/dist/surface/mcp/shared-params.js +2 -2
  438. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  439. package/dist/surface/mcp/surface-vocabulary.js +1 -0
  440. package/dist/surface/tools/account-tool.d.ts +22 -0
  441. package/dist/surface/tools/account-tool.js +157 -0
  442. package/dist/surface/tools/act/act-danger.d.ts +11 -0
  443. package/dist/surface/tools/act/act-danger.js +38 -1
  444. package/dist/surface/tools/act/act-observation.d.ts +33 -0
  445. package/dist/surface/tools/act/act-observation.js +57 -0
  446. package/dist/surface/tools/act/act-preflight.js +25 -2
  447. package/dist/surface/tools/act/act-target.js +8 -3
  448. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +8 -0
  449. package/dist/surface/tools/act/navigation/navigate-arrival.js +56 -2
  450. package/dist/surface/tools/act/resolve-target.d.ts +1 -1
  451. package/dist/surface/tools/act/resolve-target.js +18 -3
  452. package/dist/surface/tools/act/screen-request.d.ts +34 -0
  453. package/dist/surface/tools/act/screen-request.js +113 -0
  454. package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
  455. package/dist/surface/tools/act/sequence-grade.js +13 -2
  456. package/dist/surface/tools/act/settle-in-flight.js +25 -3
  457. package/dist/surface/tools/act-sequence-tool.js +138 -29
  458. package/dist/surface/tools/act-tools.d.ts +0 -9
  459. package/dist/surface/tools/act-tools.js +71 -41
  460. package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
  461. package/dist/surface/tools/agent/app-notes-learning.js +80 -0
  462. package/dist/surface/tools/agent/app-notes.d.ts +4 -0
  463. package/dist/surface/tools/agent/app-notes.js +48 -0
  464. package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
  465. package/dist/surface/tools/agent/build-staleness.js +62 -0
  466. package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
  467. package/dist/surface/tools/agent/compact-envelope.js +374 -0
  468. package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
  469. package/dist/surface/tools/agent/for-the-agent.js +44 -0
  470. package/dist/surface/tools/agent/notices.d.ts +50 -0
  471. package/dist/surface/tools/agent/notices.js +152 -0
  472. package/dist/surface/tools/agent/reteach.d.ts +11 -0
  473. package/dist/surface/tools/agent/reteach.js +50 -0
  474. package/dist/surface/tools/args/alias-args.d.ts +5 -0
  475. package/dist/surface/tools/args/alias-args.js +10 -0
  476. package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
  477. package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
  478. package/dist/surface/tools/assert/assert-source.d.ts +15 -0
  479. package/dist/surface/tools/assert/assert-source.js +40 -0
  480. package/dist/surface/tools/assert/assert-verdict.d.ts +1 -1
  481. package/dist/surface/tools/assert/assert-verdict.js +42 -17
  482. package/dist/surface/tools/browser-tools.js +51 -2
  483. package/dist/surface/tools/chat-drives.d.ts +101 -0
  484. package/dist/surface/tools/chat-drives.js +409 -0
  485. package/dist/surface/tools/coverage-tools.d.ts +2 -10
  486. package/dist/surface/tools/coverage-tools.js +2 -53
  487. package/dist/surface/tools/dynamic-tools.js +29 -1
  488. package/dist/surface/tools/empty-session-list.d.ts +51 -0
  489. package/dist/surface/tools/empty-session-list.js +120 -0
  490. package/dist/surface/tools/error-recovery.d.ts +14 -0
  491. package/dist/surface/tools/error-recovery.js +105 -8
  492. package/dist/surface/tools/explore-tools.d.ts +31 -1
  493. package/dist/surface/tools/explore-tools.js +438 -66
  494. package/dist/surface/tools/feedback-tools.d.ts +2 -3
  495. package/dist/surface/tools/feedback-tools.js +18 -3
  496. package/dist/surface/tools/gap-novelty.d.ts +3 -1
  497. package/dist/surface/tools/gap-novelty.js +8 -1
  498. package/dist/surface/tools/harness-explore.d.ts +118 -57
  499. package/dist/surface/tools/harness-explore.js +344 -292
  500. package/dist/surface/tools/harness-gate.d.ts +88 -0
  501. package/dist/surface/tools/harness-gate.js +235 -0
  502. package/dist/surface/tools/harness-hunt.d.ts +43 -0
  503. package/dist/surface/tools/harness-hunt.js +215 -0
  504. package/dist/surface/tools/harness-plan.d.ts +17 -0
  505. package/dist/surface/tools/harness-plan.js +38 -0
  506. package/dist/surface/tools/harness-script.d.ts +30 -0
  507. package/dist/surface/tools/harness-script.js +560 -0
  508. package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
  509. package/dist/surface/tools/harness-tab-guard.js +27 -0
  510. package/dist/surface/tools/harness-toolset.d.ts +22 -1
  511. package/dist/surface/tools/harness-toolset.js +101 -7
  512. package/dist/surface/tools/human-notes.d.ts +16 -0
  513. package/dist/surface/tools/human-notes.js +27 -0
  514. package/dist/surface/tools/invoke-tool.d.ts +5 -2
  515. package/dist/surface/tools/invoke-tool.js +263 -85
  516. package/dist/surface/tools/lease-hint.js +26 -5
  517. package/dist/surface/tools/lease-permissions.d.ts +33 -0
  518. package/dist/surface/tools/lease-permissions.js +73 -0
  519. package/dist/surface/tools/lease-readiness.d.ts +38 -0
  520. package/dist/surface/tools/lease-readiness.js +57 -0
  521. package/dist/surface/tools/lease-seed.d.ts +49 -0
  522. package/dist/surface/tools/lease-seed.js +167 -0
  523. package/dist/surface/tools/lease-tools.d.ts +13 -24
  524. package/dist/surface/tools/lease-tools.js +146 -197
  525. package/dist/surface/tools/live-call-text.js +4 -4
  526. package/dist/surface/tools/merge-tools.js +42 -2
  527. package/dist/surface/tools/merged-name-redirect.js +4 -0
  528. package/dist/surface/tools/next-step.d.ts +29 -0
  529. package/dist/surface/tools/next-step.js +214 -0
  530. package/dist/surface/tools/observe-tools.js +45 -9
  531. package/dist/surface/tools/platform-moment.d.ts +72 -0
  532. package/dist/surface/tools/platform-moment.js +296 -0
  533. package/dist/surface/tools/query-hint-schema.d.ts +7 -0
  534. package/dist/surface/tools/query-hint-schema.js +10 -0
  535. package/dist/surface/tools/read-tools.js +71 -14
  536. package/dist/surface/tools/real-input-attempt.js +96 -13
  537. package/dist/surface/tools/reticle-result.d.ts +120 -0
  538. package/dist/surface/tools/reticle-result.js +364 -0
  539. package/dist/surface/tools/tool-kit.d.ts +83 -3
  540. package/dist/surface/tools/tool-kit.js +77 -7
  541. package/dist/surface/tools/tools.js +43 -59
  542. package/dist/telemetry/account-envelope.d.ts +7 -0
  543. package/dist/telemetry/account-envelope.js +61 -0
  544. package/dist/telemetry/agent-client.d.ts +15 -0
  545. package/dist/telemetry/agent-client.js +57 -0
  546. package/dist/telemetry/app-instrumented.js +9 -2
  547. package/dist/telemetry/argument-shape.d.ts +7 -0
  548. package/dist/telemetry/argument-shape.js +86 -1
  549. package/dist/telemetry/automation-hint.d.ts +2 -3
  550. package/dist/telemetry/automation-hint.js +33 -8
  551. package/dist/telemetry/cli-telemetry.js +7 -2
  552. package/dist/telemetry/daemon-telemetry.js +3 -0
  553. package/dist/telemetry/error-fingerprint.d.ts +10 -1
  554. package/dist/telemetry/error-fingerprint.js +61 -5
  555. package/dist/telemetry/feedback-cli.d.ts +0 -13
  556. package/dist/telemetry/feedback-cli.js +0 -77
  557. package/dist/telemetry/feedback.d.ts +13 -0
  558. package/dist/telemetry/feedback.js +183 -17
  559. package/dist/telemetry/harness-run.d.ts +18 -0
  560. package/dist/telemetry/harness-run.js +80 -0
  561. package/dist/telemetry/hud-events.d.ts +16 -0
  562. package/dist/telemetry/hud-events.js +27 -0
  563. package/dist/telemetry/hud-metrics.d.ts +29 -6
  564. package/dist/telemetry/hud-metrics.js +116 -25
  565. package/dist/telemetry/init-telemetry.js +2 -2
  566. package/dist/telemetry/install-source.d.ts +13 -25
  567. package/dist/telemetry/install-source.js +44 -3
  568. package/dist/telemetry/mcp-connection.d.ts +0 -14
  569. package/dist/telemetry/mcp-connection.js +5 -1
  570. package/dist/telemetry/onboarding-firsts.d.ts +32 -0
  571. package/dist/telemetry/onboarding-firsts.js +81 -6
  572. package/dist/telemetry/platform-linked.d.ts +5 -0
  573. package/dist/telemetry/platform-linked.js +32 -0
  574. package/dist/telemetry/project-profile.js +5 -3
  575. package/dist/telemetry/proxy-telemetry.js +4 -1
  576. package/dist/telemetry/session-metrics.d.ts +15 -4
  577. package/dist/telemetry/session-metrics.js +88 -52
  578. package/dist/telemetry/telemetry.d.ts +38 -2
  579. package/dist/telemetry/telemetry.js +144 -34
  580. package/dist/telemetry/verification-of.d.ts +3 -1
  581. package/dist/telemetry/verification-of.js +13 -4
  582. package/dist/wire-journal.d.ts +2 -2
  583. package/dist/wire-journal.js +20 -6
  584. package/docs/README.md +3 -3
  585. package/docs/actions.mdx +6 -3
  586. package/docs/adapters.md +1 -1
  587. package/docs/agent-cheatsheet.md +25 -28
  588. package/docs/architecture.md +5 -5
  589. package/docs/autodrive.md +33 -28
  590. package/docs/benchmarks.md +5 -5
  591. package/docs/best-practices.mdx +27 -9
  592. package/docs/capabilities.mdx +14 -16
  593. package/docs/cli/affected.mdx +1 -1
  594. package/docs/cli/cloud.mdx +54 -75
  595. package/docs/cli/doctor.mdx +14 -3
  596. package/docs/cli/drive.mdx +20 -14
  597. package/docs/cli/feedback.mdx +3 -3
  598. package/docs/cli/gate.mdx +7 -2
  599. package/docs/cli/help.mdx +50 -55
  600. package/docs/cli/init.mdx +43 -28
  601. package/docs/cli/license.mdx +12 -8
  602. package/docs/cli/mcp.mdx +4 -4
  603. package/docs/cli/open.mdx +3 -3
  604. package/docs/cli/report.mdx +4 -2
  605. package/docs/cli/restart.mdx +2 -2
  606. package/docs/cli/rollback.mdx +14 -8
  607. package/docs/cli/serve.mdx +4 -4
  608. package/docs/cli/setup.mdx +23 -0
  609. package/docs/cli/status.mdx +18 -5
  610. package/docs/cli/stop.mdx +67 -8
  611. package/docs/cli/try.mdx +62 -0
  612. package/docs/cli/update.mdx +4 -3
  613. package/docs/cli/verify.mdx +17 -6
  614. package/docs/cli/version.mdx +5 -4
  615. package/docs/cli.mdx +111 -42
  616. package/docs/debugging.md +3 -3
  617. package/docs/deploy-checks.md +1 -1
  618. package/docs/desktop.mdx +9 -3
  619. package/docs/enterprise.md +11 -10
  620. package/docs/environment.mdx +7 -8
  621. package/docs/faq.mdx +3 -3
  622. package/docs/first-drive.md +5 -4
  623. package/docs/flows.md +35 -27
  624. package/docs/for-agents.md +4 -5
  625. package/docs/frameworks.mdx +32 -21
  626. package/docs/getting-started.md +65 -61
  627. package/docs/human-control.md +16 -16
  628. package/docs/index.mdx +15 -42
  629. package/docs/install-agentic.mdx +22 -37
  630. package/docs/install-manual.mdx +23 -13
  631. package/docs/instrumentation.mdx +5 -3
  632. package/docs/integration-patterns.md +7 -7
  633. package/docs/license-activation.mdx +1 -1
  634. package/docs/multi-agent-testing.md +24 -6
  635. package/docs/packages/babel-plugin.mdx +4 -2
  636. package/docs/packages/browser.mdx +16 -10
  637. package/docs/packages/core.mdx +12 -5
  638. package/docs/packages/electron.mdx +3 -2
  639. package/docs/packages/engine.mdx +1 -1
  640. package/docs/packages/eslint-plugin.mdx +18 -12
  641. package/docs/packages/next.mdx +8 -7
  642. package/docs/packages/open-verification.mdx +1 -1
  643. package/docs/packages/server.mdx +5 -5
  644. package/docs/packages/tauri.mdx +9 -9
  645. package/docs/packages/test.mdx +4 -10
  646. package/docs/packages/vite-plugin.mdx +32 -4
  647. package/docs/packages.mdx +11 -15
  648. package/docs/platform-integration.md +6 -5
  649. package/docs/predicates.mdx +40 -24
  650. package/docs/quickstart.mdx +69 -30
  651. package/docs/required-checks.md +8 -7
  652. package/docs/reticle-manual.md +15 -5
  653. package/docs/skill-file.mdx +17 -79
  654. package/docs/skill-setup.md +9 -9
  655. package/docs/skill.md +200 -0
  656. package/docs/system-map.md +7 -7
  657. package/docs/telemetry.md +23 -25
  658. package/docs/testing.md +4 -4
  659. package/docs/token-efficiency.md +12 -12
  660. package/docs/tools/act-and-wait.mdx +9 -2
  661. package/docs/tools/act-sequence.mdx +6 -7
  662. package/docs/tools/act.mdx +2 -2
  663. package/docs/tools/assert.mdx +18 -17
  664. package/docs/tools/console.mdx +9 -9
  665. package/docs/tools/inspect.mdx +16 -7
  666. package/docs/tools/look.mdx +24 -0
  667. package/docs/tools/navigate.mdx +5 -3
  668. package/docs/tools/network.mdx +2 -2
  669. package/docs/tools/observe.mdx +6 -6
  670. package/docs/tools/overview.mdx +34 -33
  671. package/docs/tools/query.mdx +5 -5
  672. package/docs/tools/session-and-feedback.mdx +22 -13
  673. package/docs/tools/sessions.mdx +8 -8
  674. package/docs/tools/snapshot.mdx +6 -5
  675. package/docs/tools/state.mdx +9 -9
  676. package/docs/tools/tools-and-run.mdx +26 -24
  677. package/docs/tools/verify.mdx +28 -0
  678. package/docs/tools/wait-for.mdx +8 -4
  679. package/docs/troubleshooting.mdx +21 -6
  680. package/docs/usage.md +55 -46
  681. package/docs/use-cases.mdx +3 -3
  682. package/docs/vs-chrome-devtools-mcp.mdx +9 -6
  683. package/docs/vs-playwright-mcp.mdx +3 -1
  684. package/docs/what-is-recorded.md +17 -5
  685. package/docs/what-is-reticle.mdx +5 -3
  686. package/docs/why-reticle.mdx +1 -1
  687. package/package.json +9 -12
  688. package/dist/command/cli/demo/demo-page.d.ts +0 -78
  689. package/dist/command/cli/demo/demo-page.js +0 -241
  690. package/dist/command/cli/demo/demo-run.d.ts +0 -38
  691. package/dist/command/cli/demo/demo-run.js +0 -138
  692. package/dist/features/harness/driver.d.ts +0 -50
  693. package/dist/features/harness/driver.js +0 -236
  694. package/dist/features/harness/jev-driver.d.ts +0 -188
  695. package/dist/features/harness/jev-driver.js +0 -678
  696. package/dist/features/harness/openai-driver.d.ts +0 -58
  697. package/dist/features/harness/openai-driver.js +0 -206
  698. package/dist/judgement/hunt/hunt-report.d.ts +0 -49
  699. package/dist/judgement/hunt/hunt-report.js +0 -84
  700. package/dist/language/flows/buddy-status.d.ts +0 -25
  701. package/dist/language/flows/buddy-status.js +0 -32
  702. package/dist/language/flows/change/watch-batcher.d.ts +0 -18
  703. package/dist/language/flows/change/watch-batcher.js +0 -19
  704. package/dist/memory/cloud/harness-offer.d.ts +0 -44
  705. package/dist/memory/cloud/harness-offer.js +0 -145
  706. package/dist/memory/project/dir/fill-value-store.d.ts +0 -32
  707. package/dist/memory/project/dir/fill-value-store.js +0 -88
  708. package/dist/surface/tools/feature-capture.d.ts +0 -151
  709. package/dist/surface/tools/feature-capture.js +0 -235
  710. package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
  711. package/dist/surface/tools/tool-hit-rate.js +0 -90
  712. package/dist/telemetry/harness-drive.d.ts +0 -4
  713. package/dist/telemetry/harness-drive.js +0 -35
  714. package/dist/telemetry/identify.d.ts +0 -22
  715. package/dist/telemetry/identify.js +0 -73
  716. package/docs/cli/capsules.mdx +0 -70
  717. package/docs/cli/hunt.mdx +0 -73
  718. package/docs/cli/identify.mdx +0 -76
  719. package/docs/cli/kill.mdx +0 -91
  720. package/docs/cli/watch.mdx +0 -62
  721. package/docs/local-registry.md +0 -143
@@ -6,75 +6,170 @@ import { HudPanel, HudView } from '@reticlehq/core';
6
6
  * Windowed like the tool histograms beside it: a periodic flush reports what happened since the
7
7
  * last one, so a dashboard sums across a session's events. Names only — every key is a control id
8
8
  * the wire schema already checked against core's closed list.
9
+ *
10
+ * Time is kept PER TAB and only while the tab is visible. Two tabs used to share one open view, so
11
+ * one tab's change closed the other's clock, and a hidden or closed tab kept accruing for as long as
12
+ * the daemon lived.
9
13
  */
10
14
  /** How many steps of the journey one window keeps. A journey longer than this is cut, and says so. */
11
15
  export const HUD_JOURNEY_MAX = 60;
16
+ /** Distinct slide ids counted per window; past this a broken notices file is not a dashboard. */
17
+ const MAX_SLIDE_IDS = 20;
18
+ const SLIDE_ID = /^[a-z0-9-]{1,56}$/;
19
+ /** The same page opened again on the same tab within this long is one view, not two. */
20
+ export const HUD_VIEW_DEDUPE_MS = 30_000;
21
+ /** A tab whose page reported HUD use without saying which tab: an older caller, or a test. */
22
+ const UNNAMED_TAB = '';
12
23
  export class HudMetrics {
13
24
  #now;
14
25
  #controls = new Map();
15
26
  #viewMs = new Map();
16
27
  #panelMs = new Map();
17
28
  #journey = [];
29
+ #slides = new Map();
30
+ #tabs = new Map();
18
31
  #cut = false;
19
32
  /** Did anything happen this window. A HUD merely sitting open is not worth a flush of its own. */
20
33
  #changed = false;
21
- #view;
22
- #panel;
23
34
  constructor(now) {
24
35
  this.#now = now;
25
36
  }
26
- record(use) {
37
+ record(use, tabId = UNNAMED_TAB) {
38
+ const tab = this.#tab(tabId);
39
+ const signals = { tabs: this.#tabs.size };
40
+ if (use.prompt !== undefined && !tab.prompted.has(use.prompt)) {
41
+ tab.prompted.add(use.prompt);
42
+ signals.prompted = use.prompt;
43
+ }
27
44
  // A control the HUD never rendered is dropped here, the one place HUD use is counted, so free
28
45
  // text cannot ride in on the id.
29
46
  const control = use.control !== undefined && isHudControl(use.control) ? use.control : undefined;
47
+ if (use.slide !== undefined)
48
+ this.#countSlide(use.slide);
30
49
  if (control === undefined && use.view === undefined && use.panel === undefined)
31
- return;
50
+ return signals;
32
51
  this.#changed = true;
33
52
  if (control !== undefined) {
34
53
  const key = use.toggle === undefined ? control : `${control}:${use.toggle}`;
35
54
  this.#controls.set(key, (this.#controls.get(key) ?? 0) + 1);
36
55
  this.#step(key);
37
56
  }
38
- if (use.view !== undefined && use.view !== this.#view?.value) {
39
- this.#close(this.#view, this.#viewMs);
40
- this.#view = { value: use.view, since: this.#now() };
57
+ const at = this.#now();
58
+ const since = tab.hidden ? undefined : at;
59
+ if (use.view !== undefined && use.view !== tab.view?.value) {
60
+ this.#close(tab.view, this.#viewMs);
61
+ tab.view = { value: use.view, since };
41
62
  this.#step(`view:${use.view}`);
42
63
  }
43
- if (use.panel !== undefined && use.panel !== this.#panel?.value) {
44
- this.#close(this.#panel, this.#panelMs);
45
- this.#panel = { value: use.panel, since: this.#now() };
46
- if (use.panel !== HudPanel.NONE)
64
+ if (use.panel !== undefined && use.panel !== tab.panel?.value) {
65
+ this.#close(tab.panel, this.#panelMs);
66
+ tab.panel = { value: use.panel, since };
67
+ if (use.panel !== HudPanel.NONE) {
47
68
  this.#step(`panel:${use.panel}`);
69
+ const last = tab.viewedAt.get(use.panel);
70
+ if (last === undefined || at - last >= HUD_VIEW_DEDUPE_MS) {
71
+ tab.viewedAt.set(use.panel, at);
72
+ signals.viewed = use.panel;
73
+ }
74
+ }
48
75
  }
76
+ return signals;
77
+ }
78
+ /** The tab went into the background: its HUD stops accruing time until it is shown again. */
79
+ hide(tabId) {
80
+ const tab = this.#tabs.get(tabId);
81
+ if (tab === undefined || tab.hidden)
82
+ return;
83
+ tab.hidden = true;
84
+ this.#pause(tab);
85
+ }
86
+ /** The tab is in front again: its HUD's view and panel accrue from now. */
87
+ show(tabId) {
88
+ const tab = this.#tabs.get(tabId);
89
+ if (tab === undefined || !tab.hidden)
90
+ return;
91
+ tab.hidden = false;
92
+ const at = this.#now();
93
+ if (tab.view !== undefined)
94
+ tab.view = { ...tab.view, since: at };
95
+ if (tab.panel !== undefined)
96
+ tab.panel = { ...tab.panel, since: at };
97
+ }
98
+ /** The tab went away: whatever it had open is counted up to now and stops. */
99
+ close(tabId) {
100
+ const tab = this.#tabs.get(tabId);
101
+ if (tab === undefined)
102
+ return;
103
+ this.#pause(tab);
104
+ this.#tabs.delete(tabId);
105
+ }
106
+ #tab(tabId) {
107
+ const known = this.#tabs.get(tabId);
108
+ if (known !== undefined)
109
+ return known;
110
+ const tab = {
111
+ view: undefined,
112
+ panel: undefined,
113
+ hidden: false,
114
+ viewedAt: new Map(),
115
+ prompted: new Set(),
116
+ };
117
+ this.#tabs.set(tabId, tab);
118
+ return tab;
119
+ }
120
+ #pause(tab) {
121
+ this.#close(tab.view, this.#viewMs);
122
+ this.#close(tab.panel, this.#panelMs);
123
+ if (tab.view !== undefined)
124
+ tab.view = { ...tab.view, since: undefined };
125
+ if (tab.panel !== undefined)
126
+ tab.panel = { ...tab.panel, since: undefined };
127
+ }
128
+ #countSlide(slide) {
129
+ // A slide id is Reticle's own (`notice-<id>` for the remote ones): lowercase words and dashes.
130
+ if (!SLIDE_ID.test(slide))
131
+ return;
132
+ if (!this.#slides.has(slide) && this.#slides.size >= MAX_SLIDE_IDS)
133
+ return;
134
+ this.#slides.set(slide, (this.#slides.get(slide) ?? 0) + 1);
135
+ this.#changed = true;
49
136
  }
50
137
  get empty() {
51
138
  return !this.#changed;
52
139
  }
53
- /** The window so far. Open view and panel are counted up to now, and keep running after. */
140
+ /** The window so far. Open views and panels are counted up to now, and keep running after. */
54
141
  summarize() {
55
- const viewMs = this.#withOpen(this.#view, this.#viewMs);
56
- const panelMs = this.#withOpen(this.#panel, this.#panelMs);
142
+ const viewMs = new Map(this.#viewMs);
143
+ const panelMs = new Map(this.#panelMs);
144
+ for (const tab of this.#tabs.values()) {
145
+ this.#close(tab.view, viewMs);
146
+ this.#close(tab.panel, panelMs);
147
+ }
57
148
  return {
58
149
  ...(this.#controls.size > 0 ? { hudControls: Object.fromEntries(this.#controls) } : {}),
59
150
  ...(viewMs.size > 0 ? { hudViewMs: Object.fromEntries(viewMs) } : {}),
60
151
  ...(panelMs.size > 0 ? { hudPanelMs: Object.fromEntries(panelMs) } : {}),
61
152
  ...(this.#journey.length > 0 ? { hudJourney: [...this.#journey] } : {}),
62
153
  ...(this.#cut ? { hudJourneyCut: true } : {}),
154
+ ...(this.#slides.size > 0 ? { hudSlides: Object.fromEntries(this.#slides) } : {}),
63
155
  };
64
156
  }
65
- /** Start the next window. The current view and panel carry over, timed from now. */
157
+ /** Start the next window. Each visible tab's view and panel carry over, timed from now. */
66
158
  reset() {
67
159
  const at = this.#now();
68
160
  this.#controls.clear();
161
+ this.#slides.clear();
69
162
  this.#viewMs.clear();
70
163
  this.#panelMs.clear();
71
164
  this.#journey.length = 0;
72
165
  this.#cut = false;
73
166
  this.#changed = false;
74
- if (this.#view !== undefined)
75
- this.#view = { ...this.#view, since: at };
76
- if (this.#panel !== undefined)
77
- this.#panel = { ...this.#panel, since: at };
167
+ for (const tab of this.#tabs.values()) {
168
+ if (tab.view?.since !== undefined)
169
+ tab.view = { ...tab.view, since: at };
170
+ if (tab.panel?.since !== undefined)
171
+ tab.panel = { ...tab.panel, since: at };
172
+ }
78
173
  }
79
174
  #step(entry) {
80
175
  if (this.#journey.length < HUD_JOURNEY_MAX)
@@ -82,14 +177,10 @@ export class HudMetrics {
82
177
  else
83
178
  this.#cut = true;
84
179
  }
180
+ /** Add an open view's time since it last started accruing; nothing while it is paused. */
85
181
  #close(open, into) {
86
- if (open === undefined)
182
+ if (open?.since === undefined)
87
183
  return;
88
184
  into.set(open.value, (into.get(open.value) ?? 0) + Math.max(0, this.#now() - open.since));
89
185
  }
90
- #withOpen(open, closed) {
91
- const out = new Map(closed);
92
- this.#close(open, out);
93
- return out;
94
- }
95
186
  }
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import { TelemetryEventKind } from '@reticlehq/core/telemetry';
14
14
  import { getTelemetry } from './telemetry.js';
15
- import { resolveInstallSource } from './install-source.js';
15
+ import { INSTALL_STATE_DIR, resolveInstallSource } from './install-source.js';
16
16
  // The failure vocabulary lives in `@reticlehq/init`, which is what classifies a failed run.
17
17
  // Re-exported so every consumer here is unchanged.
18
18
  export { InitFailure } from '@reticlehq/init';
@@ -25,7 +25,7 @@ export function reportInitOutcome(init) {
25
25
  // The other half of attribution: `reticle_installed` says a machine arrived, this says the
26
26
  // setup on it finished, and only together do they say which route converts. Same self-declared
27
27
  // marker, same refusal to infer — see install-source.ts.
28
- installSource: resolveInstallSource(),
28
+ installSource: resolveInstallSource(process.env, INSTALL_STATE_DIR),
29
29
  });
30
30
  }
31
31
  catch {
@@ -1,27 +1,3 @@
1
- /**
2
- * Which published route brought an install in.
3
- *
4
- * Four install routes ship at once and none of them could be told apart, so every question about
5
- * where distribution effort is working had no data behind it at all.
6
- *
7
- * ONE mechanism: an environment variable a channel sets on itself. Everything else that looked like
8
- * a signal turned out not to be one — see `InstallSource` in core for why `npm_config_user_agent`,
9
- * a `.claude-plugin/` directory and an installed skill folder all fail to separate these routes.
10
- *
11
- * WHICH CHANNELS ARE GENUINELY DETECTABLE TODAY, so nobody later reads a small `unknown` as success:
12
- * - `plugin` — real. The Claude Code plugin registers the MCP server itself and can set the marker
13
- * in that server's `env`, so it is carried by the process without anybody typing anything.
14
- * - `skill_file` / `npx_skill` / `docs_site` / `readme` — real ONLY where that channel's own copy
15
- * of the install command carries the marker. Each is a separately published artifact, so a copy
16
- * that has not been updated is indistinguishable from no channel at all.
17
- * - `cli_direct` — NOT detectable. Nobody types a marker to say they typed a command. It exists so
18
- * a wrapper can declare it, and until something does, a direct install reports `unknown`.
19
- *
20
- * `unknown` is therefore expected to be the largest bucket for as long as the marker is spreading,
21
- * and shrinking it is a distribution job, never a classifier job. A guessed attribution is worse
22
- * than none: it is the number distribution decisions get steered on, and a guess and a measurement
23
- * are indistinguishable once they are in the same column.
24
- */
25
1
  import { InstallSource } from '@reticlehq/core/telemetry';
26
2
  /** The one marker. Set by a channel on the process that runs the install. */
27
3
  export declare const INSTALL_SOURCE_ENV = "RETICLE_INSTALL_SOURCE";
@@ -33,7 +9,19 @@ export declare const INSTALL_SOURCE_ENV = "RETICLE_INSTALL_SOURCE";
33
9
  * whole contract is built on. Trimmed and lowercased first, because the marker travels through
34
10
  * shell snippets and copy-paste, and `Skill_File` is the same channel as `skill_file`.
35
11
  */
36
- export declare function resolveInstallSource(env?: NodeJS.ProcessEnv): InstallSource;
12
+ export declare function resolveInstallSource(env?: NodeJS.ProcessEnv,
13
+ /** Where the machine's remembered channel lives. Omitted, only the environment is read. */
14
+ stateDir?: string): InstallSource;
15
+ export declare const INSTALL_STATE_DIR: string;
16
+ /**
17
+ * Remember a declared channel for the commands that follow, which declare nothing.
18
+ *
19
+ * The one-line installer declares itself on `reticle setup install`, and the next thing that runs
20
+ * is an agent spawning `reticle mcp` with no marker at all, so without this the declaration lived
21
+ * for one process. Write-once (exclusive create): the first channel is the one that brought this
22
+ * machine in, and a later command through a different route must not take that credit.
23
+ */
24
+ export declare function rememberInstallSource(env?: NodeJS.ProcessEnv, stateDir?: string): void;
37
25
  /**
38
26
  * The install source declared by the environment, or undefined when nothing declared one.
39
27
  *
@@ -22,6 +22,9 @@
22
22
  * than none: it is the number distribution decisions get steered on, and a guess and a measurement
23
23
  * are indistinguishable once they are in the same column.
24
24
  */
25
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
26
+ import { homedir } from 'node:os';
27
+ import { join } from 'node:path';
25
28
  import { InstallSource } from '@reticlehq/core/telemetry';
26
29
  import { findProjectConfig } from '../command/cli/ports/resolve/cli-port.js';
27
30
  /** The one marker. Set by a channel on the process that runs the install. */
@@ -35,9 +38,47 @@ const KNOWN = new Set(Object.values(InstallSource));
35
38
  * whole contract is built on. Trimmed and lowercased first, because the marker travels through
36
39
  * shell snippets and copy-paste, and `Skill_File` is the same channel as `skill_file`.
37
40
  */
38
- export function resolveInstallSource(env = process.env) {
41
+ export function resolveInstallSource(env = process.env,
42
+ /** Where the machine's remembered channel lives. Omitted, only the environment is read. */
43
+ stateDir) {
39
44
  const declared = (env[INSTALL_SOURCE_ENV] ?? '').trim().toLowerCase();
40
- return KNOWN.has(declared) ? declared : InstallSource.UNKNOWN;
45
+ if (KNOWN.has(declared))
46
+ return declared;
47
+ return ((stateDir === undefined ? undefined : machineInstallSource(stateDir)) ?? InstallSource.UNKNOWN);
48
+ }
49
+ /** `~/.reticle/install-source`: the channel that installed the CLI on this machine, written once. */
50
+ const MACHINE_FILE = 'install-source';
51
+ export const INSTALL_STATE_DIR = join(homedir(), '.reticle');
52
+ function machineInstallSource(stateDir) {
53
+ try {
54
+ const recorded = readFileSync(join(stateDir, MACHINE_FILE), 'utf8').trim().toLowerCase();
55
+ return KNOWN.has(recorded) && InstallSource.UNKNOWN !== recorded
56
+ ? recorded
57
+ : undefined;
58
+ }
59
+ catch {
60
+ return undefined;
61
+ }
62
+ }
63
+ /**
64
+ * Remember a declared channel for the commands that follow, which declare nothing.
65
+ *
66
+ * The one-line installer declares itself on `reticle setup install`, and the next thing that runs
67
+ * is an agent spawning `reticle mcp` with no marker at all, so without this the declaration lived
68
+ * for one process. Write-once (exclusive create): the first channel is the one that brought this
69
+ * machine in, and a later command through a different route must not take that credit.
70
+ */
71
+ export function rememberInstallSource(env = process.env, stateDir = INSTALL_STATE_DIR) {
72
+ const declared = declaredInstallSource(env);
73
+ if (declared === undefined)
74
+ return;
75
+ try {
76
+ mkdirSync(stateDir, { recursive: true });
77
+ writeFileSync(join(stateDir, MACHINE_FILE), declared, { encoding: 'utf8', flag: 'wx' });
78
+ }
79
+ catch {
80
+ /* already remembered, or an unwritable home: attribution is never worth a failure */
81
+ }
41
82
  }
42
83
  /**
43
84
  * The install source declared by the environment, or undefined when nothing declared one.
@@ -70,7 +111,7 @@ export function projectInstallSource(cwd, env = process.env) {
70
111
  if (KNOWN.has(narrowed))
71
112
  return narrowed;
72
113
  }
73
- return resolveInstallSource(env);
114
+ return resolveInstallSource(env, INSTALL_STATE_DIR);
74
115
  }
75
116
  /**
76
117
  * `configWithInstallSource` lives in `@reticlehq/init` — `init` is the only thing that writes
@@ -1,18 +1,4 @@
1
1
  export declare function markDaemonStart(now: number): void;
2
- /**
3
- * Report one client attaching, BY NAME. Best-effort; a metric must never affect whether a client
4
- * connects.
5
- *
6
- * The name is required in practice, and that is the fix rather than a restriction. `connection.client`
7
- * was defined and documented and never once populated: the only caller reported from
8
- * `transport.connect().then(...)`, which resolves when the SSE stream opens — before the client has
9
- * said who it is. `clientInfo` first exists at `oninitialized`, so the event carried every fact about
10
- * the connection except the one that makes "which agent client converts best" answerable at all.
11
- *
12
- * An unnamed call reports NOTHING. A stream that opens and never completes a handshake is a probe,
13
- * not a client attaching, and counting it was what put an unattributable row exactly where the name
14
- * belonged.
15
- */
16
2
  export declare function reportMcpConnected(client?: string, now?: () => number): void;
17
3
  /** Tests only. */
18
4
  export declare function resetMcpConnections(): void;
@@ -33,6 +33,8 @@ export function markDaemonStart(now) {
33
33
  * not a client attaching, and counting it was what put an unattributable row exactly where the name
34
34
  * belonged.
35
35
  */
36
+ /** `connection.client`'s wire cap. */
37
+ const MAX_CLIENT_NAME = 64;
36
38
  export function reportMcpConnected(client, now = () => Date.now()) {
37
39
  if (client === undefined)
38
40
  return;
@@ -45,7 +47,9 @@ export function reportMcpConnected(client, now = () => Date.now()) {
45
47
  // A large value on the FIRST connect is the interesting case: Reticle was started and then
46
48
  // sat there. On a reconnect it is just how long the daemon has been up.
47
49
  daemonAgeMs: daemonStartedAt === undefined ? 0 : Math.max(0, now() - daemonStartedAt),
48
- client,
50
+ // Capped at the wire limit: the client names itself, and an over-long name would fail the
51
+ // schema and drop the whole event.
52
+ client: client.slice(0, MAX_CLIENT_NAME),
49
53
  // WHAT THE AGENT HAD TO LOOK AT. Most clients that attach never call a tool, and that cohort
50
54
  // emits nothing at all — an agent that reads the instructions, learns nothing is wired and
51
55
  // stops has refused nothing, so `tool_refused` cannot see it either. Read off the same flag
@@ -1,3 +1,6 @@
1
+ import { type OnboardingStep } from '@reticlehq/core';
2
+ /** Report `step` once per process, and only if this machine + project has never reported it. */
3
+ export declare function reportFirstStep(step: OnboardingStep): void;
1
4
  /** A successful page read. The dispatcher calls this after excluding refusals and pauses. */
2
5
  export declare function noteOnboardingFirst(toolName: string): void;
3
6
  /**
@@ -36,5 +39,34 @@ export declare function noteFirstVerdict(): void;
36
39
  * chokepoint — a drive that is refused before its first action drove nothing and says nothing.
37
40
  */
38
41
  export declare function noteDriven(): void;
42
+ /**
43
+ * The FIRST_RUN rung "a verdict was produced". Once per machine + project, with how long the call
44
+ * took: it fired on every verdict, so a busy session put hundreds of first-run steps in the funnel.
45
+ */
46
+ export declare function noteVerdictProduced(elapsedMs: number): void;
39
47
  /** Arm again for the next daemon run — the tests' seam, mirroring resetAppInstrumented. */
40
48
  export declare function resetOnboardingFirsts(): void;
49
+ /**
50
+ * Has this machine + project EVER crossed this onboarding step?
51
+ *
52
+ * The in-memory flags in `onboarding-firsts.ts` and `app-instrumented.ts` answer "this daemon
53
+ * process", and a daemon restarts many times a day, so every "first" step re-fired on every start.
54
+ * The answer is kept on disk instead, as one empty marker file per step under the state home:
55
+ * `~/.reticle/firsts/<projectId>/<step>`. The exclusive create (`wx`) IS the claim, so two daemons
56
+ * racing for the same first cannot both win, and nothing is ever rewritten in place.
57
+ *
58
+ * Only while telemetry is ENABLED: a disabled emitter has nothing to report and must leave nothing
59
+ * behind on disk.
60
+ */
61
+ export declare const FirstEver: {
62
+ /** This call claimed it: the true first on this machine + project. */
63
+ readonly FIRST: "first";
64
+ /** Claimed before, by this process or an earlier one. Report nothing. */
65
+ readonly SEEN: "seen";
66
+ /** The state home cannot be written. Report per process, and never claim a first. */
67
+ readonly UNKNOWN: "unknown";
68
+ };
69
+ export type FirstEver = (typeof FirstEver)[keyof typeof FirstEver];
70
+ /** Tests only: a state home and project of the test's own. `undefined` restores the default. */
71
+ export declare function setFirstEverDir(dir: string | undefined, projectId?: string): void;
72
+ export declare function claimFirstEver(step: string, projectId?: string): FirstEver;
@@ -1,5 +1,9 @@
1
- import { ActionType, OnboardingPhase, OnboardingStepStatus, ReticleTool } from '@reticlehq/core';
1
+ import { ActionType, OnboardingPhase, OnboardingStepStatus, ReticleTool, } from '@reticlehq/core';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
2
5
  import { reportOnboardingStep } from './onboarding-funnel.js';
6
+ import { getTelemetry, projectFingerprint } from './telemetry.js';
3
7
  /**
4
8
  * The onboarding steps only the DAEMON can answer, reported once each per run.
5
9
  *
@@ -13,13 +17,25 @@ import { reportOnboardingStep } from './onboarding-funnel.js';
13
17
  *
14
18
  * The flags are module state rather than per-session, because a person who tours in one tab and
15
19
  * drives in another has still only crossed each of these once.
20
+ *
21
+ * And once per MACHINE + PROJECT on top of that: the flags only know this process, and a daemon
22
+ * restarts many times a day, so each first re-fired on every start. `claimFirstEver` answers from
23
+ * disk; the true first carries `firstEver: true`, and a home that cannot be written falls back to
24
+ * once per process without the flag.
16
25
  */
17
- const seen = { look: false, act: false, verdict: false, driven: false };
26
+ const seen = { look: false, act: false, verdict: false, driven: false, verdictProduced: false };
27
+ /** Report `step` once per process, and only if this machine + project has never reported it. */
28
+ export function reportFirstStep(step) {
29
+ const ever = claimFirstEver(step.step);
30
+ if (FirstEver.SEEN === ever)
31
+ return;
32
+ void reportOnboardingStep({ ...step, ...(FirstEver.FIRST === ever ? { firstEver: true } : {}) });
33
+ }
18
34
  /** A successful page read. The dispatcher calls this after excluding refusals and pauses. */
19
35
  export function noteOnboardingFirst(toolName) {
20
36
  if (!seen.look && LOOK_TOOLS.has(toolName)) {
21
37
  seen.look = true;
22
- void reportOnboardingStep({
38
+ reportFirstStep({
23
39
  phase: OnboardingPhase.ONBOARD,
24
40
  step: 'first_look',
25
41
  status: OnboardingStepStatus.COMPLETED,
@@ -49,7 +65,7 @@ export function noteActed(args) {
49
65
  if (seen.act)
50
66
  return;
51
67
  seen.act = true;
52
- void reportOnboardingStep({
68
+ reportFirstStep({
53
69
  phase: OnboardingPhase.ONBOARD,
54
70
  step: 'first_act',
55
71
  status: OnboardingStepStatus.COMPLETED,
@@ -65,7 +81,7 @@ export function noteFirstVerdict() {
65
81
  if (seen.verdict)
66
82
  return;
67
83
  seen.verdict = true;
68
- void reportOnboardingStep({
84
+ reportFirstStep({
69
85
  phase: OnboardingPhase.ONBOARD,
70
86
  step: 'first_verdict',
71
87
  status: OnboardingStepStatus.COMPLETED,
@@ -87,12 +103,27 @@ export function noteDriven() {
87
103
  if (seen.driven)
88
104
  return;
89
105
  seen.driven = true;
90
- void reportOnboardingStep({
106
+ reportFirstStep({
91
107
  phase: OnboardingPhase.FIRST_RUN,
92
108
  step: 'driven',
93
109
  status: OnboardingStepStatus.COMPLETED,
94
110
  });
95
111
  }
112
+ /**
113
+ * The FIRST_RUN rung "a verdict was produced". Once per machine + project, with how long the call
114
+ * took: it fired on every verdict, so a busy session put hundreds of first-run steps in the funnel.
115
+ */
116
+ export function noteVerdictProduced(elapsedMs) {
117
+ if (seen.verdictProduced)
118
+ return;
119
+ seen.verdictProduced = true;
120
+ reportFirstStep({
121
+ phase: OnboardingPhase.FIRST_RUN,
122
+ step: 'verdict_produced',
123
+ status: OnboardingStepStatus.COMPLETED,
124
+ elapsedMs: Math.max(0, Math.round(elapsedMs)),
125
+ });
126
+ }
96
127
  /** Looking: the tools that read the page without changing it. */
97
128
  const LOOK_TOOLS = new Set([
98
129
  ReticleTool.SNAPSHOT,
@@ -106,4 +137,48 @@ export function resetOnboardingFirsts() {
106
137
  seen.act = false;
107
138
  seen.verdict = false;
108
139
  seen.driven = false;
140
+ seen.verdictProduced = false;
141
+ }
142
+ /**
143
+ * Has this machine + project EVER crossed this onboarding step?
144
+ *
145
+ * The in-memory flags in `onboarding-firsts.ts` and `app-instrumented.ts` answer "this daemon
146
+ * process", and a daemon restarts many times a day, so every "first" step re-fired on every start.
147
+ * The answer is kept on disk instead, as one empty marker file per step under the state home:
148
+ * `~/.reticle/firsts/<projectId>/<step>`. The exclusive create (`wx`) IS the claim, so two daemons
149
+ * racing for the same first cannot both win, and nothing is ever rewritten in place.
150
+ *
151
+ * Only while telemetry is ENABLED: a disabled emitter has nothing to report and must leave nothing
152
+ * behind on disk.
153
+ */
154
+ export const FirstEver = {
155
+ /** This call claimed it: the true first on this machine + project. */
156
+ FIRST: 'first',
157
+ /** Claimed before, by this process or an earlier one. Report nothing. */
158
+ SEEN: 'seen',
159
+ /** The state home cannot be written. Report per process, and never claim a first. */
160
+ UNKNOWN: 'unknown',
161
+ };
162
+ const DEFAULT_DIR = join(homedir(), '.reticle', 'firsts');
163
+ const EEXIST = 'EEXIST';
164
+ let override;
165
+ let cachedProjectId;
166
+ /** Tests only: a state home and project of the test's own. `undefined` restores the default. */
167
+ export function setFirstEverDir(dir, projectId = 'test-project') {
168
+ override = dir === undefined ? undefined : { dir, projectId };
169
+ }
170
+ const currentProjectId = () => (cachedProjectId ??= projectFingerprint(process.cwd()).projectId);
171
+ export function claimFirstEver(step, projectId) {
172
+ if (override === undefined && !getTelemetry().enabled)
173
+ return FirstEver.UNKNOWN;
174
+ try {
175
+ const dir = join(override?.dir ?? DEFAULT_DIR, projectId ?? override?.projectId ?? currentProjectId());
176
+ mkdirSync(dir, { recursive: true });
177
+ writeFileSync(join(dir, step), '', { flag: 'wx' });
178
+ return FirstEver.FIRST;
179
+ }
180
+ catch (error) {
181
+ const code = 'object' === typeof error && null !== error ? error.code : undefined;
182
+ return EEXIST === code ? FirstEver.SEEN : FirstEver.UNKNOWN;
183
+ }
109
184
  }
@@ -0,0 +1,5 @@
1
+ import { type PlatformLinked } from '../command/cli/cloud-link.js';
2
+ import { type Telemetry } from './telemetry.js';
3
+ export declare function reportPlatformLinked(link: PlatformLinked, telemetry?: Pick<Telemetry, 'emit'>): void;
4
+ /** Report every link this process makes. Installed once, by the entry point. */
5
+ export declare function installPlatformLinkTelemetry(): void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `platform_linked`: a project on this machine was bound to a workspace.
3
+ *
4
+ * The join between OSS usage and the platform. The platform holds who is in each workspace; this
5
+ * event, and every event after it from a linked project, carries only the workspace's pseudonymous
6
+ * id. So a person is never named on this side, and the two sides still meet on `workspaceId`.
7
+ *
8
+ * Once per NEW binding: re-running `link` against the project a repo already reports to (rotating a
9
+ * key, an agent retrying) is not a link being created.
10
+ */
11
+ import { PlatformLinkVia, TelemetryEventKind } from '@reticlehq/core/telemetry';
12
+ import { onPlatformLinked } from '../command/cli/cloud-link.js';
13
+ import { getTelemetry } from './telemetry.js';
14
+ export function reportPlatformLinked(link, telemetry = getTelemetry()) {
15
+ if (link.priorProjectId === link.projectId)
16
+ return;
17
+ try {
18
+ void telemetry.emit(TelemetryEventKind.PLATFORM_LINKED, {
19
+ link: { via: link.via },
20
+ ...(link.workspaceId === undefined ? {} : { workspaceId: link.workspaceId }),
21
+ // A CLI command exits straight after; the daemon (a HUD link) sends in-process.
22
+ detach: PlatformLinkVia.HUD !== link.via,
23
+ });
24
+ }
25
+ catch {
26
+ /* a metric never fails a link */
27
+ }
28
+ }
29
+ /** Report every link this process makes. Installed once, by the entry point. */
30
+ export function installPlatformLinkTelemetry() {
31
+ onPlatformLinked((link) => reportPlatformLinked(link));
32
+ }
@@ -16,6 +16,7 @@ import { ProjectSize, ReticleDir } from '@reticlehq/core';
16
16
  import { gitFacts } from './git-facts.js';
17
17
  import { detectStack } from './feedback-context.js';
18
18
  import { readProjectId } from '../command/cli/ports/resolve/cli-port.js';
19
+ import { DRIVE_RECORD_SUFFIX } from '../memory/project/dir/reticle-dir.js';
19
20
  /**
20
21
  * The feature FAMILIES, and the on-disk evidence that a project has adopted each. Named rather than
21
22
  * counted so the vocabulary stays stable as tools are added, renamed, or merged into action-dispatched
@@ -134,9 +135,9 @@ const defaultReadDir = (dir) => {
134
135
  };
135
136
  const defaultRead = (path) => readFileSync(path, 'utf8');
136
137
  /** How many entries a `.reticle` subdirectory holds, matching an optional extension. 0 when absent. */
137
- function countIn(reticleRoot, subdir, extension) {
138
+ function countIn(reticleRoot, subdir, extension, except) {
138
139
  try {
139
- const names = readdirSync(join(reticleRoot, subdir));
140
+ const names = readdirSync(join(reticleRoot, subdir)).filter((n) => except === undefined || !n.endsWith(except));
140
141
  return extension === undefined ? names.length : names.filter((n) => extension.test(n)).length;
141
142
  }
142
143
  catch {
@@ -158,7 +159,8 @@ export function profileProject(cwd, now, install) {
158
159
  const baselineCount = countIn(reticleRoot, ReticleDir.BASELINES_SUBDIR);
159
160
  // `.diff.png` files are diff OUTPUT, not baselines — counting them would double every visual user.
160
161
  const visualBaselineCount = countIn(reticleRoot, ReticleDir.VISUAL_SUBDIR, /(?<!\.diff)\.png$/);
161
- const runCount = countIn(reticleRoot, ReticleDir.RUNS_SUBDIR, /\.json$/);
162
+ // A drive's own record sits beside its run and is not one.
163
+ const runCount = countIn(reticleRoot, ReticleDir.RUNS_SUBDIR, /\.json$/, DRIVE_RECORD_SUFFIX);
162
164
  const capsuleCount = countIn(reticleRoot, ReticleDir.CAPSULES_SUBDIR, /\.json$/);
163
165
  const hasContract = exists(join(reticleRoot, ReticleDir.CONTRACT_FILE));
164
166
  const hasHistory = exists(join(reticleRoot, ReticleDir.PROJECT_FILE));
@@ -16,6 +16,9 @@ export async function flushProxySessionMetrics(telemetry = getTelemetry(), metri
16
16
  if (metrics.empty)
17
17
  return;
18
18
  await telemetry.emit(TelemetryEventKind.SESSION_PROGRESS, {
19
- session: metrics.summarize(true),
19
+ // A window, not the final roll-up: this rides `session_progress`, which every query sums, and a
20
+ // final summary would also resolve calls and stamp an end reason for a process that is not the
21
+ // daemon whose session this is.
22
+ session: metrics.summarize(false),
20
23
  });
21
24
  }