@reticlehq/server 2.11.0 → 2.13.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 (513) hide show
  1. package/SKILL.md +93 -111
  2. package/dist/bridge/auth-failure-reason.d.ts +6 -2
  3. package/dist/bridge/auth-failure-reason.js +15 -8
  4. package/dist/bridge/bridge.d.ts +18 -14
  5. package/dist/bridge/bridge.js +248 -165
  6. package/dist/bridge/bridge.test-harness.d.ts +2 -0
  7. package/dist/bridge/bridge.test-harness.js +8 -1
  8. package/dist/bridge/pairing-token.d.ts +5 -0
  9. package/dist/bridge/pairing-token.js +28 -0
  10. package/dist/bridge/protocol-skew.d.ts +37 -0
  11. package/dist/bridge/protocol-skew.js +45 -0
  12. package/dist/capsule/capsule-store.d.ts +8 -7
  13. package/dist/capsule/capsule-store.js +1 -1
  14. package/dist/capsule/causal-summary.d.ts +2 -2
  15. package/dist/capsule/causal-summary.js +7 -2
  16. package/dist/chromium-launch-options.d.ts +28 -0
  17. package/dist/chromium-launch-options.js +29 -0
  18. package/dist/cli/cli-doctor.js +39 -1
  19. package/dist/cli/cli-flow-commands.d.ts +3 -1
  20. package/dist/cli/cli-flow-commands.js +34 -4
  21. package/dist/cli/cli-kill.d.ts +2 -1
  22. package/dist/cli/cli-launch.d.ts +11 -1
  23. package/dist/cli/cli-launch.js +59 -10
  24. package/dist/cli/cli-parse.d.ts +17 -1
  25. package/dist/cli/cli-parse.js +238 -12
  26. package/dist/cli/cli-update-commands.js +20 -1
  27. package/dist/cli/cli-verify.d.ts +2 -1
  28. package/dist/cli/cli-verify.js +5 -4
  29. package/dist/cli/cloud-cli.js +428 -253
  30. package/dist/cli/cloud-keystore.d.ts +37 -0
  31. package/dist/cli/cloud-keystore.js +97 -0
  32. package/dist/cli/cloud-kit.d.ts +37 -0
  33. package/dist/cli/cloud-kit.js +121 -0
  34. package/dist/cli/cloud-login.d.ts +17 -0
  35. package/dist/cli/cloud-login.js +183 -0
  36. package/dist/cli/cloud-session.d.ts +48 -0
  37. package/dist/cli/cloud-session.js +53 -0
  38. package/dist/cli/config-discovery.d.ts +2 -1
  39. package/dist/cli/config-discovery.js +1 -0
  40. package/dist/cli/daemon-start-options.d.ts +32 -0
  41. package/dist/cli/daemon-start-options.js +38 -0
  42. package/dist/cli/device-flow.d.ts +76 -0
  43. package/dist/cli/device-flow.js +56 -0
  44. package/dist/cli/doctor-rows.d.ts +12 -1
  45. package/dist/cli/doctor-rows.js +12 -1
  46. package/dist/cli/drive-attach.d.ts +13 -0
  47. package/dist/cli/drive-attach.js +18 -0
  48. package/dist/cli/drive-command.js +7 -3
  49. package/dist/cli/gate-exit.d.ts +21 -0
  50. package/dist/cli/gate-exit.js +21 -0
  51. package/dist/cli/gate-hook-message.d.ts +33 -0
  52. package/dist/cli/gate-hook-message.js +46 -0
  53. package/dist/cli/mcp-command.d.ts +25 -0
  54. package/dist/cli/mcp-command.js +174 -0
  55. package/dist/cli/project-name.d.ts +30 -0
  56. package/dist/cli/project-name.js +44 -0
  57. package/dist/cli/sibling-ports.d.ts +37 -0
  58. package/dist/cli/sibling-ports.js +56 -0
  59. package/dist/cli/status-next-action.d.ts +14 -0
  60. package/dist/cli/status-next-action.js +4 -4
  61. package/dist/cli.d.ts +1 -0
  62. package/dist/cli.js +167 -186
  63. package/dist/cloud/cloud-config.d.ts +24 -2
  64. package/dist/cloud/cloud-config.js +64 -5
  65. package/dist/cloud/cloud-sync.d.ts +16 -5
  66. package/dist/cloud/cloud-sync.js +64 -1
  67. package/dist/cloud/sync-cycle.d.ts +110 -0
  68. package/dist/cloud/sync-cycle.js +270 -0
  69. package/dist/cloud/sync-daemon.d.ts +69 -0
  70. package/dist/cloud/sync-daemon.js +260 -0
  71. package/dist/cloud/sync-disk.d.ts +9 -0
  72. package/dist/cloud/sync-disk.js +191 -0
  73. package/dist/cloud/sync-hash.d.ts +2 -0
  74. package/dist/cloud/sync-hash.js +35 -0
  75. package/dist/crawl/crawl-tools.js +5 -3
  76. package/dist/crawl/crawl.d.ts +12 -13
  77. package/dist/crawl/crawl.js +25 -3
  78. package/dist/daemon/daemon-resolve.d.ts +122 -0
  79. package/dist/daemon/daemon-resolve.js +187 -0
  80. package/dist/daemon/dev-servers.d.ts +15 -0
  81. package/dist/daemon/dev-servers.js +68 -0
  82. package/dist/daemon/free-port.d.ts +29 -0
  83. package/dist/daemon/free-port.js +55 -0
  84. package/dist/daemon/heartbeat.d.ts +0 -4
  85. package/dist/daemon/heartbeat.js +2 -2
  86. package/dist/daemon/wake-decision.d.ts +32 -0
  87. package/dist/daemon/wake-decision.js +38 -0
  88. package/dist/dev/stale-issue-guard.d.ts +0 -2
  89. package/dist/dev/stale-issue-guard.js +1 -1
  90. package/dist/domain/domain-tools.js +2 -2
  91. package/dist/events/contradictions.d.ts +59 -0
  92. package/dist/events/contradictions.js +253 -27
  93. package/dist/events/declared.d.ts +17 -0
  94. package/dist/events/declared.js +43 -1
  95. package/dist/events/echo-mismatch.js +65 -1
  96. package/dist/events/event-filters.d.ts +8 -0
  97. package/dist/events/event-filters.js +19 -2
  98. package/dist/events/observed-in-window.d.ts +12 -0
  99. package/dist/events/observed-in-window.js +17 -0
  100. package/dist/events/predicate-asks.d.ts +13 -0
  101. package/dist/events/predicate-asks.js +22 -0
  102. package/dist/events/predicate-eval.js +117 -27
  103. package/dist/events/predicate-parse.d.ts +0 -6
  104. package/dist/events/predicate-parse.js +43 -3
  105. package/dist/events/predicate-precheck.d.ts +8 -0
  106. package/dist/events/predicate-precheck.js +41 -0
  107. package/dist/events/predicate-route.d.ts +14 -0
  108. package/dist/events/predicate-route.js +36 -8
  109. package/dist/events/predicate-schema.d.ts +44 -0
  110. package/dist/events/predicate-schema.js +126 -23
  111. package/dist/events/predicate.d.ts +20 -0
  112. package/dist/events/predicate.js +77 -6
  113. package/dist/events/split-text-miss.d.ts +30 -0
  114. package/dist/events/split-text-miss.js +44 -0
  115. package/dist/flows/coverage.d.ts +0 -8
  116. package/dist/flows/coverage.js +1 -1
  117. package/dist/flows/decision.d.ts +5 -1
  118. package/dist/flows/decision.js +33 -7
  119. package/dist/flows/flake.d.ts +6 -8
  120. package/dist/flows/flake.js +1 -1
  121. package/dist/flows/flow-intent.d.ts +52 -0
  122. package/dist/flows/flow-intent.js +123 -0
  123. package/dist/flows/flow-memory-consult.d.ts +55 -0
  124. package/dist/flows/flow-memory-consult.js +38 -0
  125. package/dist/flows/flow-replay-run.d.ts +18 -7
  126. package/dist/flows/flow-replay-run.js +263 -23
  127. package/dist/flows/flow-replay.js +11 -2
  128. package/dist/flows/flow-step-runners.d.ts +8 -0
  129. package/dist/flows/flow-step-runners.js +8 -2
  130. package/dist/flows/flow-store-for-session.d.ts +31 -0
  131. package/dist/flows/flow-store-for-session.js +33 -0
  132. package/dist/flows/flow-success.js +15 -3
  133. package/dist/flows/flow-tools.js +87 -33
  134. package/dist/flows/flows.d.ts +14 -1
  135. package/dist/flows/flows.js +502 -24
  136. package/dist/flows/heal-run.js +5 -2
  137. package/dist/flows/predicate-to-expect.d.ts +17 -12
  138. package/dist/flows/predicate-to-expect.js +36 -15
  139. package/dist/flows/recordings.d.ts +13 -1
  140. package/dist/flows/recordings.js +17 -2
  141. package/dist/flows/replay.d.ts +22 -2
  142. package/dist/flows/replay.js +28 -2
  143. package/dist/flows/server-verify.js +16 -2
  144. package/dist/flows/verify-change-tools.js +9 -3
  145. package/dist/honesty/blind-spots.d.ts +51 -15
  146. package/dist/honesty/blind-spots.js +64 -4
  147. package/dist/honesty/feature-capture.d.ts +150 -0
  148. package/dist/honesty/feature-capture.js +233 -0
  149. package/dist/honesty/honesty.d.ts +24 -0
  150. package/dist/honesty/honesty.js +25 -1
  151. package/dist/honesty/instrumentation-gaps.d.ts +39 -6
  152. package/dist/honesty/instrumentation-gaps.js +88 -5
  153. package/dist/honesty/tool-hit-rate.d.ts +86 -0
  154. package/dist/honesty/tool-hit-rate.js +90 -0
  155. package/dist/honesty/undeclared-change.d.ts +51 -0
  156. package/dist/honesty/undeclared-change.js +56 -0
  157. package/dist/honesty/verified.d.ts +16 -0
  158. package/dist/honesty/verified.js +84 -5
  159. package/dist/http-server.d.ts +0 -1
  160. package/dist/http-server.js +13 -1
  161. package/dist/hunt/hunt-report.d.ts +0 -2
  162. package/dist/hunt/hunt-report.js +1 -1
  163. package/dist/impact/impact-recorder.d.ts +31 -8
  164. package/dist/impact/impact-recorder.js +104 -21
  165. package/dist/impact/impact-store.d.ts +13 -4
  166. package/dist/impact/impact-store.js +44 -3
  167. package/dist/index.d.ts +4 -1
  168. package/dist/index.js +113 -7
  169. package/dist/init/agent-rules.d.ts +30 -6
  170. package/dist/init/agent-rules.js +54 -13
  171. package/dist/init/astro-patch.d.ts +16 -0
  172. package/dist/init/astro-patch.js +28 -0
  173. package/dist/init/capabilities.d.ts +4 -3
  174. package/dist/init/capabilities.js +23 -3
  175. package/dist/init/closing-hint.d.ts +36 -0
  176. package/dist/init/closing-hint.js +120 -0
  177. package/dist/init/confirm.d.ts +27 -28
  178. package/dist/init/confirm.js +51 -11
  179. package/dist/init/cra.d.ts +34 -5
  180. package/dist/init/cra.js +96 -25
  181. package/dist/init/csp-check.js +17 -2
  182. package/dist/init/csp-doctor.d.ts +8 -1
  183. package/dist/init/csp-doctor.js +8 -3
  184. package/dist/init/dev-script.d.ts +50 -0
  185. package/dist/init/dev-script.js +66 -0
  186. package/dist/init/format-generated.d.ts +18 -0
  187. package/dist/init/format-generated.js +39 -0
  188. package/dist/init/install-hint.d.ts +9 -0
  189. package/dist/init/install-hint.js +29 -0
  190. package/dist/init/mcp-clients.d.ts +7 -0
  191. package/dist/init/mcp-clients.js +7 -0
  192. package/dist/init/mcp.d.ts +17 -6
  193. package/dist/init/mcp.js +27 -11
  194. package/dist/init/node-io.js +11 -1
  195. package/dist/init/plan-framework.d.ts +0 -5
  196. package/dist/init/plan-framework.js +147 -37
  197. package/dist/init/plan.d.ts +30 -37
  198. package/dist/init/plan.js +50 -60
  199. package/dist/init/preflight.d.ts +28 -0
  200. package/dist/init/preflight.js +38 -0
  201. package/dist/init/refresh-rules.d.ts +39 -0
  202. package/dist/init/refresh-rules.js +51 -0
  203. package/dist/init/run.d.ts +32 -35
  204. package/dist/init/run.js +151 -202
  205. package/dist/init/slash-command.d.ts +1 -1
  206. package/dist/init/snippets.js +90 -31
  207. package/dist/init/vite-config.d.ts +1 -1
  208. package/dist/init/vite-config.js +39 -25
  209. package/dist/init/workspace-apps.d.ts +17 -10
  210. package/dist/init/workspace-apps.js +110 -0
  211. package/dist/input/network-mock-tools.js +3 -6
  212. package/dist/input/real-input.d.ts +2 -0
  213. package/dist/input/real-input.js +28 -7
  214. package/dist/input/scroll-find.d.ts +7 -4
  215. package/dist/input/scroll-find.js +63 -25
  216. package/dist/input/scroll-tools.js +4 -6
  217. package/dist/input/viewport-tools.js +5 -4
  218. package/dist/intent/inline-intent.d.ts +37 -0
  219. package/dist/intent/inline-intent.js +193 -0
  220. package/dist/intent/intent-shard-store.d.ts +72 -0
  221. package/dist/intent/intent-shard-store.js +203 -0
  222. package/dist/intent/intent-shard.d.ts +411 -0
  223. package/dist/intent/intent-shard.js +176 -0
  224. package/dist/intent/intent-store.d.ts +49 -0
  225. package/dist/intent/intent-store.js +133 -0
  226. package/dist/intent/intent-subject.d.ts +45 -0
  227. package/dist/intent/intent-subject.js +177 -0
  228. package/dist/intent/intent-tools.d.ts +2 -0
  229. package/dist/intent/intent-tools.js +143 -0
  230. package/dist/intent/open-intents.d.ts +42 -0
  231. package/dist/intent/open-intents.js +40 -0
  232. package/dist/journal/envelope.d.ts +26 -26
  233. package/dist/journal/journal-recorder.d.ts +23 -0
  234. package/dist/journal/journal-recorder.js +35 -0
  235. package/dist/license/license-env.d.ts +29 -0
  236. package/dist/license/license-env.js +148 -0
  237. package/dist/loopback-agent.d.ts +31 -0
  238. package/dist/loopback-agent.js +57 -0
  239. package/dist/mcp/attach-memory.d.ts +83 -0
  240. package/dist/mcp/attach-memory.js +185 -0
  241. package/dist/mcp/client-identity.d.ts +31 -0
  242. package/dist/mcp/client-identity.js +51 -0
  243. package/dist/mcp/mcp-post-transport.d.ts +26 -0
  244. package/dist/mcp/mcp-post-transport.js +109 -0
  245. package/dist/mcp/mcp-proxy.d.ts +2 -8
  246. package/dist/mcp/mcp-proxy.js +64 -62
  247. package/dist/mcp/mcp.d.ts +2 -0
  248. package/dist/mcp/mcp.js +52 -24
  249. package/dist/mcp/proxy-backoff.d.ts +11 -0
  250. package/dist/mcp/proxy-backoff.js +13 -0
  251. package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
  252. package/dist/mcp/proxy-daemon-probe.js +7 -1
  253. package/dist/mcp/proxy-handshake.d.ts +23 -0
  254. package/dist/mcp/proxy-handshake.js +31 -0
  255. package/dist/mcp/server-instructions.js +45 -3
  256. package/dist/mcp/tool-catalog-cache.d.ts +16 -0
  257. package/dist/mcp/tool-catalog-cache.js +22 -0
  258. package/dist/memory/memory-tools.d.ts +2 -0
  259. package/dist/memory/memory-tools.js +82 -0
  260. package/dist/memory/project-memory.d.ts +34 -0
  261. package/dist/memory/project-memory.js +96 -0
  262. package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
  263. package/dist/oracles/flow-instrument-gaps.js +4 -5
  264. package/dist/oracles/propose-consequences.js +12 -3
  265. package/dist/oracles/self-instrument.d.ts +9 -9
  266. package/dist/oracles/self-instrument.js +19 -13
  267. package/dist/pool/browser-pool.d.ts +75 -5
  268. package/dist/pool/browser-pool.js +144 -5
  269. package/dist/pool/playwright-launcher.js +12 -1
  270. package/dist/project/fs-port.d.ts +3 -0
  271. package/dist/project/fs-port.js +3 -2
  272. package/dist/project/memory-fs.js +6 -1
  273. package/dist/project/project-tools.js +6 -35
  274. package/dist/project/reticle-dir.d.ts +17 -2
  275. package/dist/project/reticle-dir.js +22 -4
  276. package/dist/runs/context-tools.d.ts +2 -0
  277. package/dist/runs/context-tools.js +89 -0
  278. package/dist/runs/run-context.d.ts +40 -0
  279. package/dist/runs/run-context.js +158 -0
  280. package/dist/runs/run-diff.d.ts +2 -1
  281. package/dist/runs/run-store.d.ts +10 -0
  282. package/dist/runs/run-store.js +10 -0
  283. package/dist/runs/verification-sync.d.ts +0 -11
  284. package/dist/runs/verification-sync.js +24 -3
  285. package/dist/runs/verify-server.d.ts +8 -1
  286. package/dist/runs/verify-server.js +14 -2
  287. package/dist/session/command-timeout.d.ts +5 -2
  288. package/dist/session/command-timeout.js +5 -4
  289. package/dist/session/dev-server-probe.d.ts +0 -8
  290. package/dist/session/dev-server-probe.js +1 -1
  291. package/dist/session/last-act.d.ts +7 -1
  292. package/dist/session/last-act.js +2 -2
  293. package/dist/session/lease-visibility.d.ts +67 -0
  294. package/dist/session/lease-visibility.js +76 -0
  295. package/dist/session/live-control-tools.js +1 -42
  296. package/dist/session/no-session-diagnosis.d.ts +33 -0
  297. package/dist/session/no-session-diagnosis.js +82 -24
  298. package/dist/session/no-session-next-action.d.ts +12 -0
  299. package/dist/session/no-session-next-action.js +77 -6
  300. package/dist/session/no-session-watch.d.ts +18 -6
  301. package/dist/session/no-session-watch.js +125 -75
  302. package/dist/session/output-budget.js +4 -1
  303. package/dist/session/pending-commands.d.ts +11 -1
  304. package/dist/session/pending-commands.js +12 -2
  305. package/dist/session/resolve-within.d.ts +34 -0
  306. package/dist/session/resolve-within.js +53 -0
  307. package/dist/session/session-health.d.ts +9 -0
  308. package/dist/session/session-health.js +23 -0
  309. package/dist/session/session-info.d.ts +18 -0
  310. package/dist/session/session-info.js +11 -0
  311. package/dist/session/session-manager.d.ts +14 -0
  312. package/dist/session/session-manager.js +111 -4
  313. package/dist/session/session-mirror.d.ts +16 -0
  314. package/dist/session/session-mirror.js +22 -0
  315. package/dist/session/session-recommendation.d.ts +19 -5
  316. package/dist/session/session-recommendation.js +25 -9
  317. package/dist/session/session-successor.d.ts +37 -0
  318. package/dist/session/session-successor.js +113 -0
  319. package/dist/session/session-tools.js +2 -2
  320. package/dist/session/session.d.ts +53 -3
  321. package/dist/session/session.js +162 -9
  322. package/dist/session/wall-clock.d.ts +12 -0
  323. package/dist/session/wall-clock.js +14 -0
  324. package/dist/setup/agent-configs.d.ts +96 -0
  325. package/dist/setup/agent-configs.js +333 -0
  326. package/dist/setup/agent-io.d.ts +9 -0
  327. package/dist/setup/agent-io.js +16 -0
  328. package/dist/setup/agent-writer.d.ts +51 -0
  329. package/dist/setup/agent-writer.js +115 -0
  330. package/dist/setup/approval-migration.d.ts +47 -0
  331. package/dist/setup/approval-migration.js +68 -0
  332. package/dist/setup/auto-approve.d.ts +85 -0
  333. package/dist/setup/auto-approve.js +150 -0
  334. package/dist/setup/bridge-port.d.ts +15 -0
  335. package/dist/setup/bridge-port.js +24 -0
  336. package/dist/setup/desktop-shape.d.ts +57 -0
  337. package/dist/setup/desktop-shape.js +63 -0
  338. package/dist/setup/dev-server-wait.d.ts +60 -0
  339. package/dist/setup/dev-server-wait.js +65 -0
  340. package/dist/setup/drive-agent.d.ts +71 -0
  341. package/dist/setup/drive-agent.js +171 -0
  342. package/dist/setup/drive-plan.d.ts +68 -0
  343. package/dist/setup/drive-plan.js +130 -0
  344. package/dist/setup/ensure-daemon.d.ts +29 -0
  345. package/dist/setup/ensure-daemon.js +58 -0
  346. package/dist/setup/init-runtime.d.ts +47 -0
  347. package/dist/setup/init-runtime.js +160 -0
  348. package/dist/setup/interrupt.d.ts +25 -0
  349. package/dist/setup/interrupt.js +40 -0
  350. package/dist/setup/license-key.d.ts +39 -0
  351. package/dist/setup/license-key.js +72 -0
  352. package/dist/setup/listeners.d.ts +37 -0
  353. package/dist/setup/listeners.js +85 -0
  354. package/dist/setup/node-effects.d.ts +60 -0
  355. package/dist/setup/node-effects.js +210 -0
  356. package/dist/setup/page-probe.d.ts +39 -0
  357. package/dist/setup/page-probe.js +49 -0
  358. package/dist/setup/relaunch.d.ts +36 -0
  359. package/dist/setup/relaunch.js +57 -0
  360. package/dist/setup/remaining-steps.d.ts +19 -0
  361. package/dist/setup/remaining-steps.js +31 -0
  362. package/dist/setup/run-setup.d.ts +99 -0
  363. package/dist/setup/run-setup.js +187 -0
  364. package/dist/setup/session-pick.d.ts +47 -0
  365. package/dist/setup/session-pick.js +51 -0
  366. package/dist/setup/setup-command.d.ts +45 -0
  367. package/dist/setup/setup-command.js +199 -0
  368. package/dist/setup/setup-options.d.ts +74 -0
  369. package/dist/setup/setup-options.js +67 -0
  370. package/dist/setup/transcripts.d.ts +16 -0
  371. package/dist/setup/transcripts.js +43 -0
  372. package/dist/status-payload.d.ts +14 -1
  373. package/dist/status-payload.js +23 -1
  374. package/dist/telemetry/automation-hint.d.ts +2 -1
  375. package/dist/telemetry/daemon-telemetry.d.ts +8 -1
  376. package/dist/telemetry/daemon-telemetry.js +52 -5
  377. package/dist/telemetry/error-class.js +1 -1
  378. package/dist/telemetry/feedback-context.d.ts +9 -1
  379. package/dist/telemetry/feedback-context.js +60 -29
  380. package/dist/telemetry/feedback-local.d.ts +15 -0
  381. package/dist/telemetry/feedback-local.js +56 -0
  382. package/dist/telemetry/feedback-outbox.d.ts +2 -1
  383. package/dist/telemetry/feedback.d.ts +11 -0
  384. package/dist/telemetry/feedback.js +19 -6
  385. package/dist/telemetry/init-telemetry.d.ts +2 -0
  386. package/dist/telemetry/init-telemetry.js +2 -0
  387. package/dist/telemetry/install-source.d.ts +14 -0
  388. package/dist/telemetry/install-source.js +30 -0
  389. package/dist/telemetry/license-activation.d.ts +7 -1
  390. package/dist/telemetry/license-activation.js +9 -5
  391. package/dist/telemetry/mcp-connection.js +7 -0
  392. package/dist/telemetry/project-profile.d.ts +12 -9
  393. package/dist/telemetry/project-profile.js +12 -7
  394. package/dist/telemetry/proxy-telemetry.d.ts +3 -0
  395. package/dist/telemetry/proxy-telemetry.js +21 -0
  396. package/dist/telemetry/run-telemetry.d.ts +0 -2
  397. package/dist/telemetry/run-telemetry.js +1 -1
  398. package/dist/telemetry/session-metrics.d.ts +12 -0
  399. package/dist/telemetry/session-metrics.js +35 -1
  400. package/dist/telemetry/telemetry.js +17 -2
  401. package/dist/telemetry/tool-refused.d.ts +3 -1
  402. package/dist/telemetry/tool-refused.js +34 -2
  403. package/dist/telemetry/verification-of.js +2 -2
  404. package/dist/tools/act-capsule.d.ts +10 -0
  405. package/dist/tools/act-capsule.js +8 -2
  406. package/dist/tools/act-danger.d.ts +2 -1
  407. package/dist/tools/act-danger.js +11 -3
  408. package/dist/tools/act-observation.d.ts +41 -0
  409. package/dist/tools/act-observation.js +36 -0
  410. package/dist/tools/act-preflight.d.ts +20 -0
  411. package/dist/tools/act-preflight.js +98 -0
  412. package/dist/tools/act-sequence-retry.d.ts +45 -0
  413. package/dist/tools/act-sequence-retry.js +58 -0
  414. package/dist/tools/act-sequence-tool.d.ts +13 -0
  415. package/dist/tools/act-sequence-tool.js +135 -0
  416. package/dist/tools/act-target.d.ts +14 -0
  417. package/dist/tools/act-target.js +47 -0
  418. package/dist/tools/act-tools.d.ts +21 -1
  419. package/dist/tools/act-tools.js +196 -204
  420. package/dist/tools/assert-source.d.ts +22 -0
  421. package/dist/tools/assert-source.js +53 -0
  422. package/dist/tools/assert-verdict.d.ts +20 -3
  423. package/dist/tools/assert-verdict.js +58 -20
  424. package/dist/tools/browser-tools.js +6 -22
  425. package/dist/tools/complete-snapshot.d.ts +48 -0
  426. package/dist/tools/complete-snapshot.js +75 -0
  427. package/dist/tools/coverage-tools.d.ts +10 -1
  428. package/dist/tools/coverage-tools.js +123 -69
  429. package/dist/tools/dynamic-tools.js +53 -13
  430. package/dist/tools/error-recovery.d.ts +25 -14
  431. package/dist/tools/error-recovery.js +78 -9
  432. package/dist/tools/feedback-tools.d.ts +1 -1
  433. package/dist/tools/feedback-tools.js +2 -2
  434. package/dist/tools/invoke-tool.js +121 -11
  435. package/dist/tools/lease-hint.d.ts +10 -0
  436. package/dist/tools/lease-hint.js +53 -4
  437. package/dist/tools/lease-tools.d.ts +35 -5
  438. package/dist/tools/lease-tools.js +191 -13
  439. package/dist/tools/merged-name-redirect.d.ts +13 -0
  440. package/dist/tools/merged-name-redirect.js +22 -0
  441. package/dist/tools/navigate-arrival.d.ts +0 -2
  442. package/dist/tools/navigate-arrival.js +1 -1
  443. package/dist/tools/numeric-bounds.d.ts +100 -0
  444. package/dist/tools/numeric-bounds.js +125 -0
  445. package/dist/tools/observe-tools.js +109 -65
  446. package/dist/tools/read-tools.js +31 -8
  447. package/dist/tools/real-input-attempt.d.ts +32 -0
  448. package/dist/tools/real-input-attempt.js +212 -2
  449. package/dist/tools/reconcile-tools.js +22 -8
  450. package/dist/tools/resolve-target.d.ts +0 -7
  451. package/dist/tools/sdk-marker-probe.d.ts +29 -0
  452. package/dist/tools/sdk-marker-probe.js +57 -0
  453. package/dist/tools/snapshot-delta.d.ts +1 -1
  454. package/dist/tools/tool-kit.d.ts +24 -0
  455. package/dist/tools/tool-kit.js +21 -0
  456. package/dist/tools/tool-names.d.ts +5 -0
  457. package/dist/tools/tool-names.js +5 -0
  458. package/dist/tools/tool-surface.d.ts +85 -6
  459. package/dist/tools/tool-surface.js +122 -2
  460. package/dist/tools/tools-helpers.d.ts +19 -0
  461. package/dist/tools/tools-helpers.js +46 -0
  462. package/dist/tools/tools.d.ts +2 -0
  463. package/dist/tools/tools.js +78 -12
  464. package/dist/tools/unadvertised-help.js +4 -4
  465. package/dist/tools/verify-next-baton.d.ts +53 -0
  466. package/dist/tools/verify-next-baton.js +52 -0
  467. package/dist/update/reticle-deps.d.ts +9 -0
  468. package/dist/update/reticle-deps.js +27 -0
  469. package/dist/update/sdk-sync.d.ts +1 -2
  470. package/dist/update/sdk-sync.js +1 -21
  471. package/dist/update/update-nudge.d.ts +25 -10
  472. package/dist/update/update-nudge.js +32 -3
  473. package/dist/update/updater.d.ts +1 -1
  474. package/dist/version/peer-announce.js +4 -2
  475. package/dist/version/sdk-fix.d.ts +47 -0
  476. package/dist/version/sdk-fix.js +127 -0
  477. package/dist/version/version-nudge.d.ts +14 -0
  478. package/dist/version/version-nudge.js +21 -0
  479. package/dist/version/version-skew.d.ts +38 -3
  480. package/dist/version/version-skew.js +83 -7
  481. package/dist/visual/visual-store.d.ts +6 -6
  482. package/dist/visual/visual-store.js +18 -16
  483. package/dist/visual/visual-tools.js +57 -12
  484. package/docs/README.md +2 -1
  485. package/docs/cli/cloud.mdx +1 -1
  486. package/docs/cli/doctor.mdx +2 -0
  487. package/docs/cli/serve.mdx +4 -2
  488. package/docs/cli/verify.mdx +4 -5
  489. package/docs/cli.mdx +7 -3
  490. package/docs/desktop.mdx +1 -1
  491. package/docs/docs.json +2 -2
  492. package/docs/gates.md +1 -1
  493. package/docs/getting-started.md +19 -3
  494. package/docs/index.mdx +9 -6
  495. package/docs/install-agentic.mdx +38 -12
  496. package/docs/install-manual.mdx +1 -1
  497. package/docs/packages/browser.mdx +2 -2
  498. package/docs/packages/server.mdx +1 -1
  499. package/docs/packages.mdx +8 -6
  500. package/docs/platform-integration.md +17 -0
  501. package/docs/predicates.mdx +8 -0
  502. package/docs/reticle-manual.md +106 -0
  503. package/docs/skill-file.mdx +3 -3
  504. package/docs/telemetry-contract.md +108 -4
  505. package/docs/tools-act-sequence.mdx +16 -0
  506. package/docs/tools-tools-and-run.mdx +2 -4
  507. package/docs/troubleshooting.mdx +9 -0
  508. package/docs/usage.md +4 -3
  509. package/package.json +4 -3
  510. package/dist/init/cursor.d.ts +0 -24
  511. package/dist/init/cursor.js +0 -80
  512. package/dist/session/session-readiness.d.ts +0 -34
  513. package/dist/session/session-readiness.js +0 -36
@@ -9,11 +9,12 @@
9
9
  * holds to. Detection is entirely best-effort: an unreadable package.json or an SDK too old to report
10
10
  * its runtime yields `undefined`, never a throw and never a guess.
11
11
  */
12
+ import { mcpClientIdentity, setMcpClientIdentityHook } from '../mcp/client-identity.js';
12
13
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
13
14
  import { isAbsolute, join } from 'node:path';
14
- import { AppRuntime, McpScope, PageDriver } from '@reticlehq/core';
15
+ import { AppRuntime, McpScope, PageDriver, StackUnknownReason, } from '@reticlehq/core';
15
16
  import { parseMajor } from '../init/detect.js';
16
- import { findWorkspaceApps } from '../init/run.js';
17
+ import { findWorkspaceApps } from '../init/workspace-apps.js';
17
18
  /**
18
19
  * Dependency → reported stack, in resolution order. Meta-frameworks come FIRST: a Next app also
19
20
  * depends on react, and reporting `react` for it would collapse the single most important segment
@@ -40,10 +41,14 @@ const PACKAGE_JSON = 'package.json';
40
41
  /** A project-scoped MCP registration lives in one of these; `reticle init` writes user scope instead. */
41
42
  const PROJECT_MCP_FILES = ['.mcp.json', join('.cursor', 'mcp.json'), join('.vscode', 'mcp.json')];
42
43
  /**
43
- * The project's framework and its MAJOR version, or `{}` when package.json is absent, unreadable, or
44
- * names nothing we recognize. The major comes from `parseMajor` — the same range parser `reticle init`
45
- * uses to decide React 19 source mapping — so a range like `^15.0.0-canary.3` narrows the same way in
46
- * both places instead of via a second, subtly different reader.
44
+ * The project's framework and its MAJOR version. The major comes from `parseMajor` — the same range
45
+ * parser `reticle init` uses to decide React 19 source mapping — so a range like `^15.0.0-canary.3`
46
+ * narrows the same way in both places instead of via a second, subtly different reader.
47
+ *
48
+ * `no-manifest` covers absent AND unreadable: both mean this directory could not answer, which is
49
+ * the distinction that matters to the caller. Unreadable is not separated because a manifest we
50
+ * cannot parse is not evidence about the app, and a reason nobody can act on is noise in a closed
51
+ * vocabulary.
47
52
  */
48
53
  function stackOfManifest(dir, read) {
49
54
  let pkg;
@@ -51,7 +56,7 @@ function stackOfManifest(dir, read) {
51
56
  pkg = JSON.parse(read(join(dir, PACKAGE_JSON)));
52
57
  }
53
58
  catch {
54
- return {};
59
+ return { kind: 'no-manifest' };
55
60
  }
56
61
  const deps = { ...pkg.devDependencies, ...pkg.dependencies };
57
62
  for (const [dep, stack] of STACK_BY_DEP) {
@@ -59,15 +64,17 @@ function stackOfManifest(dir, read) {
59
64
  if (range === undefined)
60
65
  continue;
61
66
  const stackMajor = parseMajor(range);
62
- return { stack, ...(stackMajor !== undefined ? { stackMajor } : {}) };
67
+ return { kind: 'found', stack, ...(stackMajor !== undefined ? { stackMajor } : {}) };
63
68
  }
64
- return {};
69
+ return { kind: 'unrecognised', declaresWorkspaces: pkg.workspaces !== undefined };
65
70
  }
66
71
  export function detectStack(cwd, read = readFile) {
67
72
  // The cwd wins when it IS an app — never redirect away from the real answer.
68
73
  const here = stackOfManifest(cwd, read);
69
- if (here.stack !== undefined)
70
- return { ...here, stackSource: 'cwd' };
74
+ if ('found' === here.kind) {
75
+ const { stack, stackMajor } = here;
76
+ return { stack, ...(stackMajor !== undefined ? { stackMajor } : {}), stackSource: 'cwd' };
77
+ }
71
78
  // Otherwise look for the app. The daemon's cwd is wherever the agent's client happened to launch,
72
79
  // which for a real repo is the ROOT while the app sits in `frontend/`, `web/`, or a declared
73
80
  // workspace. Reading one directory and giving up is why this returned nothing for every project
@@ -78,19 +85,53 @@ export function detectStack(cwd, read = readFile) {
78
85
  // declared workspaces (pnpm-workspace.yaml, package.json `workspaces`) AND scans top-level
79
86
  // directories, and it was widened once already for a repo shape a user hit twice. A second
80
87
  // detector here would drift from it the first time either changed.
88
+ //
89
+ // Every exit below carries WHY, because an empty result is not a cause. Four different facts used
90
+ // to arrive as one `{}`: no app anywhere, an app we read and did not recognise, workspace apps
91
+ // that were all unrecognised, and discovery erroring. They have different fixes, and the bucket
92
+ // was unusable while they were indistinguishable (#617).
93
+ let sawWorkspaceApp = false;
81
94
  try {
82
95
  for (const app of findWorkspaceApps(profileIo(cwd))) {
83
96
  const found = stackOfManifest(join(cwd, app), read);
84
97
  // Reported as `workspace` so the share of projects needing discovery is measurable — that
85
98
  // number is what says whether our inference needs an agent to correct it.
86
- if (found.stack !== undefined)
87
- return { ...found, stackSource: 'workspace' };
99
+ if ('found' === found.kind) {
100
+ const { stack, stackMajor } = found;
101
+ return {
102
+ stack,
103
+ ...(stackMajor !== undefined ? { stackMajor } : {}),
104
+ stackSource: 'workspace',
105
+ };
106
+ }
107
+ // A manifest we READ and did not recognise is evidence about the app; a directory with no
108
+ // manifest is not an app at all and says nothing either way.
109
+ if ('unrecognised' === found.kind)
110
+ sawWorkspaceApp = true;
88
111
  }
89
112
  }
90
113
  catch {
91
114
  // Discovery touches the filesystem; a permission error must never cost us the profile event.
115
+ // Named rather than folded into "found nothing": an error is not an absence, and a detector
116
+ // that reports one as the other sends the reader looking for a project that was there.
117
+ return { stackUnknownReason: StackUnknownReason.DISCOVERY_FAILED };
118
+ }
119
+ if (sawWorkspaceApp) {
120
+ return { stackUnknownReason: StackUnknownReason.WORKSPACE_APPS_UNRECOGNISED };
92
121
  }
93
- return {};
122
+ // The cwd's own manifest is the stronger statement when it exists: we read this app and did not
123
+ // know it. Ranked below the workspace answer only because discovery may have found the REAL app.
124
+ if ('unrecognised' === here.kind) {
125
+ // A monorepo root naming no framework is not an unrecognised APP — the app is a directory down
126
+ // and discovery did not surface it. Reporting MANIFEST_UNRECOGNISED here would read as "add
127
+ // this framework to the table" about a manifest that describes no framework at all.
128
+ return {
129
+ stackUnknownReason: here.declaresWorkspaces
130
+ ? StackUnknownReason.WORKSPACE_ROOT_NO_APPS
131
+ : StackUnknownReason.MANIFEST_UNRECOGNISED,
132
+ };
133
+ }
134
+ return { stackUnknownReason: StackUnknownReason.NO_APP_FOUND };
94
135
  }
95
136
  /**
96
137
  * The three filesystem methods `findWorkspaceApps` needs, rooted at `cwd`.
@@ -150,26 +191,16 @@ function fileExists(path) {
150
191
  * client's own claim about itself, while an env-var table is a guess that silently rots every time a
151
192
  * vendor renames a variable. Unset (a CLI run with no MCP peer) simply reports no client.
152
193
  */
153
- let clientNameHook;
194
+ /** Kept as the telemetry-facing name; the mechanism itself lives in mcp/client-identity.ts. */
154
195
  export function setMcpClientNameHook(hook) {
155
- clientNameHook = hook;
196
+ setMcpClientIdentityHook(hook);
156
197
  }
157
- /**
158
- * The client's own name and version from its handshake.
159
- *
160
- * Note what is NOT here and cannot be: the MODEL. MCP's `clientInfo` carries a name, a title and a
161
- * version and has no concept of a model, so the transport genuinely cannot tell us. The agent
162
- * self-reports it on the feedback itself, which is the only mechanism available and a reliable one
163
- * given the report is already something the agent authored.
164
- */
165
198
  function detectClient() {
166
199
  try {
167
- const info = clientNameHook?.();
168
- const name = info?.name;
169
- const version = info?.version;
200
+ const info = mcpClientIdentity();
170
201
  return {
171
- ...(name !== undefined && name !== '' ? { client: name.slice(0, 64) } : {}),
172
- ...(version !== undefined && version !== '' ? { clientVersion: version.slice(0, 32) } : {}),
202
+ ...(info.name === undefined ? {} : { client: info.name }),
203
+ ...(info.version === undefined ? {} : { clientVersion: info.version }),
173
204
  };
174
205
  }
175
206
  catch {
@@ -0,0 +1,15 @@
1
+ import type { Feedback } from '@reticlehq/core';
2
+ export interface SavedFeedback {
3
+ path: string;
4
+ /** A ready-to-run command that turns the file into an issue. */
5
+ command: string;
6
+ }
7
+ /**
8
+ * Write the report under `<cwd>/.reticle/feedback/`. Returns undefined if it cannot be written —
9
+ * a feedback save that throws would take down the tool call that was reporting a problem, which is
10
+ * the one outcome worse than losing the report.
11
+ */
12
+ export declare function saveFeedbackLocally(cwd: string, input: {
13
+ kind?: string;
14
+ source?: string;
15
+ }, text: string, context: Partial<Feedback>, now?: number): SavedFeedback | undefined;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Where a feedback report goes when it cannot be sent.
3
+ *
4
+ * The report is fully written and redacted by the time delivery is refused. Throwing it away
5
+ * punishes exactly the behaviour the channel exists to encourage — and it hits hardest where the
6
+ * reports are best, because a Reticle source checkout disables telemetry by cwd, so the contributor
7
+ * who can name the file and the line is the one person who cannot file at all.
8
+ *
9
+ * Writing it down costs nothing and loses nothing. The receipt then carries a path and a command,
10
+ * so the human is handed one thing to run instead of being asked to retype what an agent already
11
+ * wrote.
12
+ */
13
+ import { mkdirSync, writeFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { ReticleDir } from '@reticlehq/core';
16
+ /** Reports live beside the rest of the workspace, not in a temp dir somebody has to be told about. */
17
+ const FEEDBACK_SUBDIR = 'feedback';
18
+ /** Filesystem-safe, sortable, and unique enough for one report per second per kind. */
19
+ const stamp = (now) => new Date(now).toISOString().replace(/[:.]/g, '-').replace('Z', '');
20
+ const asMarkdown = (input, text, context) => {
21
+ const title = `Reticle feedback: ${input.kind ?? 'note'}`;
22
+ return [
23
+ `# ${title}`,
24
+ '',
25
+ text.trim(),
26
+ '',
27
+ '## Context',
28
+ '',
29
+ '```json',
30
+ JSON.stringify(context, null, 2),
31
+ '```',
32
+ '',
33
+ ].join('\n');
34
+ };
35
+ /**
36
+ * Write the report under `<cwd>/.reticle/feedback/`. Returns undefined if it cannot be written —
37
+ * a feedback save that throws would take down the tool call that was reporting a problem, which is
38
+ * the one outcome worse than losing the report.
39
+ */
40
+ export function saveFeedbackLocally(cwd, input, text, context, now = Date.now()) {
41
+ try {
42
+ const dir = join(cwd, ReticleDir.ROOT, FEEDBACK_SUBDIR);
43
+ mkdirSync(dir, { recursive: true });
44
+ const path = join(dir, `${stamp(now)}-${input.kind ?? 'note'}.md`);
45
+ writeFileSync(path, asMarkdown(input, text, context), 'utf8');
46
+ return {
47
+ path,
48
+ // `--body-file` rather than an inlined body: the report contains newlines, quotes and JSON,
49
+ // and a command somebody has to repair before running is a command they do not run.
50
+ command: `gh issue create --repo reticlehq/reticle --title "Reticle feedback: ${input.kind ?? 'note'}" --body-file "${path}"`,
51
+ };
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
@@ -1,5 +1,5 @@
1
1
  /** One queued report: the payload, plus what is needed to find and remove it again. */
2
- export interface OutboxEntry {
2
+ interface OutboxEntry {
3
3
  id: string;
4
4
  t: string;
5
5
  payload: unknown;
@@ -20,3 +20,4 @@ export declare function readOutbox(home?: string): OutboxEntry[];
20
20
  * Volume is a handful of lines a week, so the cost is irrelevant.
21
21
  */
22
22
  export declare function markDelivered(id: string | null, home?: string): void;
23
+ export {};
@@ -55,6 +55,17 @@ export interface FeedbackReceipt {
55
55
  */
56
56
  accepted: boolean;
57
57
  reason?: string;
58
+ /**
59
+ * Where the report was written when it could not be SENT.
60
+ *
61
+ * A refusal used to be the end of the road: the report was already written by then, and throwing
62
+ * it away punished exactly the behaviour this channel exists to encourage. Worse, the commonest
63
+ * refusal is a Reticle SOURCE CHECKOUT — so the reporter best placed to name a file and a line was
64
+ * the only one who could not file at all.
65
+ */
66
+ savedTo?: string;
67
+ /** A ready-to-run command that turns the saved file into an issue. */
68
+ fileWith?: string;
58
69
  /** Which redaction rules fired. Empty when nothing needed removing. */
59
70
  redacted: string[];
60
71
  /** The exact context that went with it, echoed back so the send is never a black box. */
@@ -26,6 +26,7 @@ import { platform } from 'node:os';
26
26
  import { getTelemetry } from './telemetry.js';
27
27
  import { noteFeedbackUndelivered } from './feedback-delivery.js';
28
28
  import { isReticleSourceCheckout } from './dev-repo.js';
29
+ import { saveFeedbackLocally } from './feedback-local.js';
29
30
  import { SERVER_VERSION } from '../version/server-version.js';
30
31
  import { feedbackContext } from './feedback-context.js';
31
32
  import { markDelivered, outboxPath, queueFeedback } from './feedback-outbox.js';
@@ -140,15 +141,27 @@ export async function submitFeedback(input, opts = {}) {
140
141
  }
141
142
  const telemetry = getTelemetry();
142
143
  if (!telemetry.enabled) {
144
+ /*
145
+ * Cannot SEND is not the same as cannot FILE.
146
+ *
147
+ * The report is fully written and redacted by this point. Discarding it punishes the exact
148
+ * behaviour this channel exists to encourage, and it hits hardest where the reports are best: a
149
+ * Reticle source checkout disables telemetry by cwd, so a contributor who can name the file and
150
+ * the line was the one person who could not file at all.
151
+ *
152
+ * So it goes to disk and the receipt says where, plus a command that turns it into an issue.
153
+ * Nothing is lost, and the human is handed one thing to run rather than asked to retype it.
154
+ */
155
+ const cwd = opts.cwd ?? process.cwd();
156
+ const saved = saveFeedbackLocally(cwd, input, body.text, context);
143
157
  return {
144
158
  sent: false,
145
159
  accepted: false,
146
- // Named precisely, because the commonest cause is not what the old wording suggested: a
147
- // Reticle SOURCE CHECKOUT disables telemetry by cwd, and that is exactly where release runs and
148
- // contributor sessions happen — so the reports most worth having were the ones silently lost.
149
- reason: isReticleSourceCheckout(opts.cwd ?? process.cwd())
150
- ? 'this is a Reticle source checkout, where telemetry is disabled by design, so feedback has nowhere to go. Open an issue at https://github.com/reticlehq/reticle/issues instead, or run from the app you are verifying.'
151
- : `telemetry is disabled on this machine, so feedback has nowhere to go. Re-enable with \`reticle telemetry enable\`, or open an issue at https://github.com/reticlehq/reticle/issues.`,
160
+ // Named precisely, because the commonest cause is not what the old wording suggested.
161
+ reason: isReticleSourceCheckout(cwd)
162
+ ? 'this is a Reticle source checkout, where telemetry is disabled by design, so this could not be sent — it has been written to disk instead, and nothing was lost.'
163
+ : `telemetry is disabled on this machine, so this could not be sent — it has been written to disk instead. Re-enable with \`reticle telemetry enable\` to send directly.`,
164
+ ...(saved === undefined ? {} : { savedTo: saved.path, fileWith: saved.command }),
152
165
  redacted,
153
166
  context,
154
167
  };
@@ -15,6 +15,8 @@ import { type InitOutcome } from '@reticlehq/core';
15
15
  export declare const InitFailure: {
16
16
  /** Run outside a project root. The single most common first-run mistake. */
17
17
  readonly NO_PACKAGE_JSON: "no_package_json";
18
+ /** The manifest exists and is not valid JSON — a trailing comma, usually. Never a stack trace. */
19
+ readonly MALFORMED_PACKAGE_JSON: "malformed_package_json";
18
20
  /** The package manager failed: offline, a locked registry, a broken install. */
19
21
  readonly DEPENDENCY_INSTALL: "dependency_install";
20
22
  /** The `claude mcp add` step failed — the CLI is missing or refused. Reticle installs but is unreachable. */
@@ -17,6 +17,8 @@ import { resolveInstallSource } from './install-source.js';
17
17
  export const InitFailure = {
18
18
  /** Run outside a project root. The single most common first-run mistake. */
19
19
  NO_PACKAGE_JSON: 'no_package_json',
20
+ /** The manifest exists and is not valid JSON — a trailing comma, usually. Never a stack trace. */
21
+ MALFORMED_PACKAGE_JSON: 'malformed_package_json',
20
22
  /** The package manager failed: offline, a locked registry, a broken install. */
21
23
  DEPENDENCY_INSTALL: 'dependency_install',
22
24
  /** The `claude mcp add` step failed — the CLI is missing or refused. Reticle installs but is unreachable. */
@@ -54,3 +54,17 @@ export declare function declaredInstallSource(env?: NodeJS.ProcessEnv): InstallS
54
54
  * data, and a hand-edited one must not be able to put an arbitrary string on the wire.
55
55
  */
56
56
  export declare function projectInstallSource(cwd: string, env?: NodeJS.ProcessEnv): InstallSource;
57
+ /**
58
+ * The same config with `installSource` added, or `undefined` when there is nothing to add.
59
+ *
60
+ * `installSource` is written only when `.reticle.json` is CREATED, and `init` reports that file as
61
+ * `already exists` on every re-run — so everyone who was already here when the marker shipped, and
62
+ * everyone who ran `init` once without one, keeps `unknown` forever with no way to fix it. That is
63
+ * most of the population the field exists to describe.
64
+ *
65
+ * An EXISTING value is never overwritten. The first channel is the one that brought the user in, and
66
+ * a later `init` through a different route must not take credit for an acquisition it did not make.
67
+ * Anything unparseable is left exactly as it is: a config is user data, and `init` rewriting a file
68
+ * it could not read would be a far worse failure than a missing dimension.
69
+ */
70
+ export declare function configWithInstallSource(source: string | null | undefined, declared: string | undefined): string | undefined;
@@ -72,3 +72,33 @@ export function projectInstallSource(cwd, env = process.env) {
72
72
  }
73
73
  return resolveInstallSource(env);
74
74
  }
75
+ /**
76
+ * The same config with `installSource` added, or `undefined` when there is nothing to add.
77
+ *
78
+ * `installSource` is written only when `.reticle.json` is CREATED, and `init` reports that file as
79
+ * `already exists` on every re-run — so everyone who was already here when the marker shipped, and
80
+ * everyone who ran `init` once without one, keeps `unknown` forever with no way to fix it. That is
81
+ * most of the population the field exists to describe.
82
+ *
83
+ * An EXISTING value is never overwritten. The first channel is the one that brought the user in, and
84
+ * a later `init` through a different route must not take credit for an acquisition it did not make.
85
+ * Anything unparseable is left exactly as it is: a config is user data, and `init` rewriting a file
86
+ * it could not read would be a far worse failure than a missing dimension.
87
+ */
88
+ export function configWithInstallSource(source, declared) {
89
+ if (undefined === declared || null === source || undefined === source)
90
+ return undefined;
91
+ let parsed;
92
+ try {
93
+ parsed = JSON.parse(source);
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ if (null === parsed || 'object' !== typeof parsed || Array.isArray(parsed))
99
+ return undefined;
100
+ const fields = parsed;
101
+ if (undefined !== fields['installSource'])
102
+ return undefined;
103
+ return `${JSON.stringify({ ...fields, installSource: declared }, null, 2)}\n`;
104
+ }
@@ -30,6 +30,12 @@ import { LicenseActivation } from '@reticlehq/core';
30
30
  export interface LicenseFacts {
31
31
  licenseId?: string;
32
32
  licenseStatus?: LicenseActivation;
33
+ /**
34
+ * A key was placed, whether or not this build could judge it. See LICENSE_KEY_PRESENT in core.
35
+ * Reported even in evaluation mode, which is the whole point: that is the case where every other
36
+ * licence property is deliberately silent.
37
+ */
38
+ licenseKeyPresent?: boolean;
33
39
  }
34
40
  /**
35
41
  * Resolve activation for one event. The clock is passed in (rule 7) and is the EVENT's clock, not a
@@ -45,4 +51,4 @@ export interface LicenseFacts {
45
51
  * Never throws. A telemetry property may not change behaviour (rule 5), and this one sits in front of
46
52
  * key parsing, which is the part most likely to be handed something malformed.
47
53
  */
48
- export declare function licenseFacts(now: number, env?: NodeJS.ProcessEnv): LicenseFacts;
54
+ export declare function licenseFacts(now: number, env?: NodeJS.ProcessEnv, baked?: string): LicenseFacts;
@@ -26,7 +26,7 @@
26
26
  * holds a customer list.
27
27
  */
28
28
  import { LicenseActivation } from '@reticlehq/core';
29
- import { describeLicense } from '../license/license.js';
29
+ import { describeLicense, LICENSE_KEY_ENV } from '../license/license.js';
30
30
  /**
31
31
  * Resolve activation for one event. The clock is passed in (rule 7) and is the EVENT's clock, not a
32
32
  * value captured at startup: sessions here run to eleven hours, so a key that expires mid-session must
@@ -41,13 +41,17 @@ import { describeLicense } from '../license/license.js';
41
41
  * Never throws. A telemetry property may not change behaviour (rule 5), and this one sits in front of
42
42
  * key parsing, which is the part most likely to be handed something malformed.
43
43
  */
44
- export function licenseFacts(now, env = process.env) {
44
+ export function licenseFacts(now, env = process.env, baked) {
45
45
  try {
46
- const report = describeLicense(now, env);
47
- if (LicenseActivation.EVAL === report.status)
48
- return {};
46
+ const report = baked === undefined ? describeLicense(now, env) : describeLicense(now, env, baked);
47
+ // Asked BEFORE the eval short-circuit, because eval is exactly the case that used to be silent.
48
+ const keyPresent = (env[LICENSE_KEY_ENV] ?? '').length > 0;
49
+ if (LicenseActivation.EVAL === report.status) {
50
+ return keyPresent ? { licenseKeyPresent: true } : {};
51
+ }
49
52
  return {
50
53
  licenseStatus: report.status,
54
+ ...(keyPresent ? { licenseKeyPresent: true } : {}),
51
55
  ...(report.licenseId !== undefined ? { licenseId: report.licenseId } : {}),
52
56
  };
53
57
  }
@@ -11,6 +11,7 @@
11
11
  */
12
12
  import { TelemetryEventKind } from '@reticlehq/core';
13
13
  import { getTelemetry } from './telemetry.js';
14
+ import { appEverConnected } from './app-instrumented.js';
14
15
  let connectsSeen = 0;
15
16
  /** Set when the daemon starts, so the FIRST connect can report how long Reticle sat unused. */
16
17
  let daemonStartedAt;
@@ -30,6 +31,12 @@ export function reportMcpConnected(client, now = () => Date.now()) {
30
31
  // sat there. On a reconnect it is just how long the daemon has been up.
31
32
  daemonAgeMs: daemonStartedAt === undefined ? 0 : Math.max(0, now() - daemonStartedAt),
32
33
  ...(client !== undefined ? { client } : {}),
34
+ // WHAT THE AGENT HAD TO LOOK AT. Most clients that attach never call a tool, and that cohort
35
+ // emits nothing at all — an agent that reads the instructions, learns nothing is wired and
36
+ // stops has refused nothing, so `tool_refused` cannot see it either. Read off the same flag
37
+ // `app_instrumented` sets, never a second counter, so the two halves of the funnel cannot
38
+ // disagree about one daemon run.
39
+ appConnected: appEverConnected(),
33
40
  },
34
41
  });
35
42
  }
@@ -13,9 +13,6 @@ export declare const FeatureFamily: {
13
13
  readonly BUG_CAPSULES: "bug_capsules";
14
14
  readonly CROSS_RUN_HISTORY: "cross_run_history";
15
15
  };
16
- export type FeatureFamily = (typeof FeatureFamily)[keyof typeof FeatureFamily];
17
- /** Count source files, stopping at the cap (the cap itself lands in the largest bucket, correctly). */
18
- export declare function countSourceFiles(root: string, readDir?: DirReader): number;
19
16
  export declare function sizeBucket(sourceFiles: number): ProjectSize;
20
17
  /**
21
18
  * Project age in whole WEEKS, from the repo's first commit.
@@ -30,15 +27,21 @@ export declare function sizeBucket(sourceFiles: number): ProjectSize;
30
27
  export declare function projectAgeWeeks(root: string, now: number, read?: FileReader): number | undefined;
31
28
  /** True when package.json declares workspaces — monorepos exercise very different code paths. */
32
29
  export declare function isMonorepo(root: string, read?: FileReader): boolean;
33
- interface DirEntry {
34
- name: string;
35
- isDirectory: boolean;
36
- }
37
- type DirReader = (dir: string) => DirEntry[];
38
30
  type FileReader = (path: string) => string;
39
31
  /**
40
32
  * Build the full profile. Best-effort throughout: an unreadable project yields a profile of zeroes
41
33
  * rather than an exception, because a telemetry snapshot must never be able to fail a daemon start.
42
34
  */
43
- export declare function profileProject(cwd: string, now: number): ProjectProfile;
35
+ /**
36
+ * Facts about the install that the filesystem cannot answer.
37
+ *
38
+ * Passed in rather than read here for the same reason `previouslyConnected` is passed to the MCP
39
+ * server: it needs the daemon's port and the state home, and a profile that reached for those would
40
+ * stop being a pure function of a directory.
41
+ */
42
+ export interface InstallFacts {
43
+ /** Has an app for this project EVER connected to Reticle, from durable state. */
44
+ appConnectedBefore: boolean;
45
+ }
46
+ export declare function profileProject(cwd: string, now: number, install?: InstallFacts): ProjectProfile;
44
47
  export {};
@@ -15,6 +15,7 @@ import { join } from 'node:path';
15
15
  import { ProjectSize, ReticleDir } from '@reticlehq/core';
16
16
  import { gitFacts } from './git-facts.js';
17
17
  import { detectStack } from './feedback-context.js';
18
+ import { readProjectId } from '../cli/cli-port.js';
18
19
  /**
19
20
  * The feature FAMILIES, and the on-disk evidence that a project has adopted each. Named rather than
20
21
  * counted so the vocabulary stays stable as tools are added, renamed, or merged into action-dispatched
@@ -45,7 +46,7 @@ const MAX_FILES_SCANNED = 6000;
45
46
  const MAX_WALK_DEPTH = 8;
46
47
  const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000;
47
48
  /** Count source files, stopping at the cap (the cap itself lands in the largest bucket, correctly). */
48
- export function countSourceFiles(root, readDir = defaultReadDir) {
49
+ function countSourceFiles(root, readDir = defaultReadDir) {
49
50
  let count = 0;
50
51
  const walk = (dir, depth) => {
51
52
  if (depth > MAX_WALK_DEPTH || count >= MAX_FILES_SCANNED)
@@ -151,11 +152,7 @@ function exists(path) {
151
152
  return false;
152
153
  }
153
154
  }
154
- /**
155
- * Build the full profile. Best-effort throughout: an unreadable project yields a profile of zeroes
156
- * rather than an exception, because a telemetry snapshot must never be able to fail a daemon start.
157
- */
158
- export function profileProject(cwd, now) {
155
+ export function profileProject(cwd, now, install) {
159
156
  const reticleRoot = join(cwd, ReticleDir.ROOT);
160
157
  const flowCount = countIn(reticleRoot, ReticleDir.FLOWS_SUBDIR, /\.json$/);
161
158
  const baselineCount = countIn(reticleRoot, ReticleDir.BASELINES_SUBDIR);
@@ -180,7 +177,7 @@ export function profileProject(cwd, now) {
180
177
  featuresUsed.push(FeatureFamily.BUG_CAPSULES);
181
178
  if (hasHistory)
182
179
  featuresUsed.push(FeatureFamily.CROSS_RUN_HISTORY);
183
- const { stack, stackMajor, stackSource } = detectStack(cwd);
180
+ const { stack, stackMajor, stackSource, stackUnknownReason } = detectStack(cwd);
184
181
  const ageWeeks = projectAgeWeeks(cwd, now);
185
182
  const git = gitFacts(cwd);
186
183
  return {
@@ -189,6 +186,8 @@ export function profileProject(cwd, now) {
189
186
  ...(stack !== undefined ? { stack } : {}),
190
187
  ...(stackSource !== undefined ? { stackSource } : {}),
191
188
  ...(stackMajor !== undefined ? { stackMajor } : {}),
189
+ // Present only when `stack` is absent, so the field's presence marks the unknown bucket.
190
+ ...(stackUnknownReason !== undefined ? { stackUnknownReason } : {}),
192
191
  size: sizeBucket(countSourceFiles(cwd)),
193
192
  monorepo: isMonorepo(cwd),
194
193
  ...(ageWeeks !== undefined ? { ageWeeks } : {}),
@@ -202,5 +201,11 @@ export function profileProject(cwd, now) {
202
201
  // Rounded to 2dp: the exact ratio is a function of the counts above, and an unrounded float would
203
202
  // just be a higher-cardinality restatement of them.
204
203
  featureDepth: Math.round((featuresUsed.length / ALL_FAMILIES.length) * 100) / 100,
204
+ // WHERE the install stopped, for the daemons that never see an app. `daemon_started` minus
205
+ // `app_instrumented` says one happened and cannot say why; these two bits split that silence
206
+ // into "never ran init", "ran it and no page has ever reached us", and "works, just not up
207
+ // right now". See the fields on ProjectProfileSchema.
208
+ initialized: readProjectId(cwd) !== undefined,
209
+ ...(install === undefined ? {} : { appConnectedBefore: install.appConnectedBefore }),
205
210
  };
206
211
  }
@@ -0,0 +1,3 @@
1
+ import { type SessionMetrics } from './session-metrics.js';
2
+ import { type Telemetry } from './telemetry.js';
3
+ export declare function flushProxySessionMetrics(telemetry?: Telemetry, metrics?: SessionMetrics): Promise<void>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The MCP proxy's session rollup.
3
+ *
4
+ * POST socket failures (`ENOBUFS`, `EMFILE`, `ECONNREFUSED` before any bytes were sent) happen in
5
+ * this process, not the daemon's. `tool_refused` never fires (the handler never ran) and
6
+ * `mcp_connection_lost` never fires (the SSE stream is fine). Counts live on SessionMetrics here
7
+ * and leave as one `session_progress` — an existing kind, omitted when nothing happened.
8
+ *
9
+ * The send is awaited. Fire-and-forget microseconds before `process.exit` is how `daemon_stopped`
10
+ * never arrived: the POST was killed every time and nothing threw.
11
+ */
12
+ import { TelemetryEventKind } from '@reticlehq/core';
13
+ import { getSessionMetrics } from './session-metrics.js';
14
+ import { getTelemetry } from './telemetry.js';
15
+ export async function flushProxySessionMetrics(telemetry = getTelemetry(), metrics = getSessionMetrics()) {
16
+ if (metrics.empty)
17
+ return;
18
+ await telemetry.emit(TelemetryEventKind.SESSION_PROGRESS, {
19
+ session: metrics.summarize(true),
20
+ });
21
+ }
@@ -11,8 +11,6 @@
11
11
  * how a replay works.
12
12
  */
13
13
  import { type ReticleVerificationRun } from '@reticlehq/core';
14
- /** The tool name reported for this path, so CI runs are distinguishable from agent-driven ones. */
15
- export declare const VERIFY_SURFACE = "reticle_verify";
16
14
  /**
17
15
  * Report one completed verification run: the verdict, and one bug per failing flow.
18
16
  *
@@ -14,7 +14,7 @@ import { BugAttribution, BugSource, RunFlowStatus, TelemetryActor, TelemetryEven
14
14
  import { getSessionMetrics } from './session-metrics.js';
15
15
  import { getTelemetry } from './telemetry.js';
16
16
  /** The tool name reported for this path, so CI runs are distinguishable from agent-driven ones. */
17
- export const VERIFY_SURFACE = 'reticle_verify';
17
+ const VERIFY_SURFACE = 'reticle_verify';
18
18
  /**
19
19
  * Report one completed verification run: the verdict, and one bug per failing flow.
20
20
  *
@@ -65,6 +65,18 @@ export declare class SessionMetrics {
65
65
  * one inside a much larger pile. Same treatment otherwise — fingerprinted, variables stripped.
66
66
  */
67
67
  recordSdkFailure(site: string, message: string): void;
68
+ /**
69
+ * The MCP proxy could not POST a tool call because the TCP socket itself failed (`ENOBUFS`,
70
+ * `EMFILE`, `ECONNREFUSED` before any bytes left). The SSE stream is still up, so this is
71
+ * invisible to `mcp_connection_lost`, and the handler never ran, so it is invisible to
72
+ * `tool_refused`. Counted so a dashboard can see it without a new event kind.
73
+ */
74
+ recordPostSocketFailure(): void;
75
+ /**
76
+ * A bounded retry of an unsent POST then delivered. The pair against `recordPostSocketFailure`:
77
+ * the retry that quietly saved a call is a different fact from the one that never ran.
78
+ */
79
+ recordPostRetrySaved(): void;
68
80
  /** The agent's recent approach run + the call in flight — context for a crash report. */
69
81
  get trail(): {
70
82
  breadcrumb: string[];