@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
@@ -0,0 +1,199 @@
1
+ /**
2
+ * The wire: everything init established, plus the world, plus the phases.
3
+ *
4
+ * Kept apart from cli.ts so the assembly is readable in one place and so `handleInit` stays the
5
+ * three lines it was. The only decision here is which effects to hand over; the sequencing lives in
6
+ * run-setup.ts and the pieces it calls.
7
+ */
8
+ import { existsSync, readFileSync } from 'node:fs';
9
+ import { homedir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ import { planAgentConfigs } from './agent-configs.js';
12
+ import { AppShape, readShape } from './desktop-shape.js';
13
+ import { stopOnInterrupt } from './interrupt.js';
14
+ import { applyAgentPlan, applyAgentSkills } from './agent-writer.js';
15
+ import { ApprovalOutcome, grantAutoApproval } from './auto-approve.js';
16
+ import { agentIo } from './agent-io.js';
17
+ import { EnsureDaemon, ensureDaemon, nodeEnsureDaemonDeps } from './ensure-daemon.js';
18
+ import { openInBrowser } from '../cli/cli-launch.js';
19
+ import { chooseDriver, DRIVERS, shouldEscalate } from './drive-plan.js';
20
+ import { driveWith } from './drive-agent.js';
21
+ import { binaryExists, flowsSaved, listSessions, OwnedDevServer, probePage, } from './node-effects.js';
22
+ import { runSetupPhases, } from './run-setup.js';
23
+ /** The capabilities file init scaffolds, in the order a project is likely to have it. */
24
+ const CAPABILITY_FILES = ['reticle-dev.tsx', 'reticle-dev.ts', 'reticle-dev.jsx', 'reticle-dev.js'];
25
+ /** `electron` in either dependency list, read the same way init's desktop doctor reads it. */
26
+ function hasElectronDependency(dir) {
27
+ try {
28
+ const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
29
+ if ('object' !== typeof pkg || null === pkg)
30
+ return false;
31
+ const p = pkg;
32
+ return undefined !== { ...p.dependencies, ...p.devDependencies }['electron'];
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ }
38
+ /**
39
+ * Register with the agents `init` does not reach, and leave the skill where one loads skills from.
40
+ *
41
+ * init covers eight clients and only where it finds them installed, which leaves VS Code's USER
42
+ * scope unwritten — it exists on machines today, and init only ever writes the project-scope file,
43
+ * so a VS Code user has no tools outside the directory they ran init in.
44
+ */
45
+ export function registerOtherAgents(print) {
46
+ const platform = process.platform;
47
+ const home = homedir();
48
+ const results = applyAgentPlan(planAgentConfigs({ home, platform, exists: agentIo.exists, readFile: agentIo.readFile }), agentIo);
49
+ const wrote = results.filter((r) => 'created' === r.action || 'merged' === r.action);
50
+ if (0 < wrote.length) {
51
+ print(`registered the MCP server with ${wrote.length} more agent(s): ${wrote.map((r) => r.name).join(', ')}`);
52
+ }
53
+ // A format we will not rewrite is somebody's to edit, so it has to be said rather than skipped.
54
+ for (const manual of results.filter((r) => 'manual' === r.action)) {
55
+ print(`${manual.name}: ${manual.why} — add the reticle entry to ${manual.file} by hand.`);
56
+ }
57
+ const skills = applyAgentSkills(agentIo, { home, platform });
58
+ if (0 < skills.length)
59
+ print(`wrote the /reticle skill for ${skills.length} agent(s)`);
60
+ // Registration alone still leaves an Accept dialog in front of every call, and a verification run
61
+ // makes dozens of them: the loop is only autonomous once the tools are pre-approved.
62
+ const approvals = grantAutoApproval(agentIo, { home, platform });
63
+ const granted = approvals.filter((a) => ApprovalOutcome.GRANTED === a.outcome);
64
+ if (0 < granted.length) {
65
+ print(`pre-approved the reticle tools in ${granted.map((a) => a.name).join(', ')} — no Accept prompt per call`);
66
+ }
67
+ for (const noted of approvals.filter((a) => undefined !== a.warn)) {
68
+ print(`${noted.name}: ${String(noted.warn)}`);
69
+ }
70
+ }
71
+ /**
72
+ * Run the phases against the real world.
73
+ *
74
+ * The dev server is stopped on every ending except success, where it is handed to the user: an
75
+ * instrumented app they can watch is the deliverable, and killing it would leave them with config
76
+ * files and a dead tab.
77
+ */
78
+ export async function runSetupCommand(input, print) {
79
+ if (input.registerAgents)
80
+ registerOtherAgents(print);
81
+ // Before the app is booted, because the app's whole job from here is to dial this port. Without
82
+ // it the phases wait out their budget and then report the SDK as the thing that failed.
83
+ const daemon = await ensureDaemon(input.bridgePort, nodeEnsureDaemonDeps());
84
+ if (EnsureDaemon.UNAVAILABLE === daemon) {
85
+ print(`could not start the Reticle daemon on port ${String(input.bridgePort)}, so nothing is listening for the app to connect to. Run \`npx @reticlehq/server serve --port ${String(input.bridgePort)}\` and try again.`);
86
+ }
87
+ // Which shell this is decides three things the phases would otherwise get wrong: opening a
88
+ // browser (harmful for desktop, where the app's own window is the client), waiting for an HTTP
89
+ // port (a Tauri webview has none), and how long the app gets to appear (a cold `tauri dev` builds
90
+ // Rust first). The same evidence init's desktop doctor reads.
91
+ const shape = readShape({
92
+ hasTauriConf: existsSync(join(input.appDir, 'src-tauri', 'tauri.conf.json')),
93
+ hasElectronDep: hasElectronDependency(input.appDir),
94
+ });
95
+ if (AppShape.WEB !== shape)
96
+ print(`detected a ${shape} app`);
97
+ const server = new OwnedDevServer();
98
+ // Both roots, because in a monorepo `.reticle/` sits at the app root rather than where setup ran.
99
+ const flowRoots = [input.invokedAt, input.appDir];
100
+ let lastDrive;
101
+ let escalated;
102
+ const effects = {
103
+ startDevServer: (command, cwd) => {
104
+ print(`starting: ${command}`);
105
+ server.start(command, cwd, input.env);
106
+ return Promise.resolve();
107
+ },
108
+ devServerOutput: () => server.output(),
109
+ devServerExited: () => server.exited(),
110
+ devServerQuietForMs: () => server.quietForMs(),
111
+ observedPorts: () => server.listeningPorts(),
112
+ probePage,
113
+ openBrowser: async (url) => {
114
+ const failure = await openInBrowser(url);
115
+ if (null !== failure) {
116
+ print(`could not open a browser (${failure}). On a machine with none — CI, a container, an SSH ` +
117
+ 'session — take a tab Reticle owns instead: reticle_run({ tool: "reticle_lease", args: ' +
118
+ `{ action: "acquire", url: "${url}" } }).`);
119
+ }
120
+ },
121
+ listSessions: () => listSessions(input.bridgePort),
122
+ // Asked before the drive rather than inferred from its result: "nothing to drive with" and
123
+ // "drove and proved nothing" are different answers and only the second is a failure.
124
+ driverAvailable: () => null !==
125
+ chooseDriver(DRIVERS, (bin) => ({ present: binaryExists(bin), runs: binaryExists(bin) })),
126
+ drive: (url, session) => {
127
+ const driver = chooseDriver(DRIVERS, (bin) => ({
128
+ present: binaryExists(bin),
129
+ // Present is not enough: a CLI that does not run produces an empty session that looks
130
+ // exactly like success.
131
+ runs: binaryExists(bin),
132
+ }));
133
+ if (null === driver)
134
+ return Promise.resolve(null);
135
+ // Only when the SESSION says they were never finished. init fills this file for a
136
+ // conventional app — it detects a state library and the testids — so opening it otherwise
137
+ // spends a turn on work already done, and grants write access nobody needed.
138
+ const capabilitiesFile = false === session.hasCapabilities
139
+ ? CAPABILITY_FILES.map((f) => join(input.appDir, 'src', f)).find((p) => existsSync(p))
140
+ : undefined;
141
+ print('');
142
+ print(` ▸ WATCH ${url} NOW — the HUD is on, and you are about to see Reticle drive your app.`);
143
+ print('');
144
+ const request = {
145
+ url,
146
+ sessionId: session.sessionId,
147
+ tabThrottled: false,
148
+ budgetUsd: input.driveBudgetUsd,
149
+ ...(undefined === input.flow ? {} : { flow: input.flow }),
150
+ ...(undefined === input.driveModel ? {} : { model: input.driveModel }),
151
+ ...(undefined === capabilitiesFile ? {} : { unfinishedCapabilitiesFile: capabilitiesFile }),
152
+ };
153
+ lastDrive = driveWith(driver, request, input.appDir);
154
+ // A weak flow only ACTS, so it passes when the feature is broken — and setup replays saved
155
+ // flows, which turns one weak recording into a permanent green. Re-record it rather than
156
+ // hand the trade to the user.
157
+ if (shouldEscalate({
158
+ escalationEnabled: input.escalateWeakFlow,
159
+ fasterModel: input.driveModel,
160
+ flowSaved: flowsSaved(flowRoots),
161
+ ...(undefined === lastDrive.grade ? { grade: undefined } : { grade: lastDrive.grade }),
162
+ })) {
163
+ print(`the saved flow graded \`${lastDrive.grade ?? 'unknown'}\` — re-recording with the default model`);
164
+ const stronger = driveWith(driver, { ...request, model: undefined }, input.appDir);
165
+ escalated = { from: lastDrive.grade ?? 'unknown', to: stronger.grade ?? 'unknown' };
166
+ if (undefined !== stronger.grade)
167
+ lastDrive = stronger;
168
+ }
169
+ if (undefined !== lastDrive.incomplete) {
170
+ print(`the drive did not finish, and ${lastDrive.incomplete}`);
171
+ }
172
+ return Promise.resolve('' === lastDrive.text ? null : lastDrive.text);
173
+ },
174
+ flowsSaved: () => flowsSaved(flowRoots),
175
+ now: () => Date.now(),
176
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
177
+ note: print,
178
+ };
179
+ // A `finally` does not run on SIGINT, and this phase owns a detached dev server — see interrupt.ts.
180
+ const releaseSignals = stopOnInterrupt(() => {
181
+ server.stop();
182
+ }, process);
183
+ try {
184
+ const outcome = await runSetupPhases({ ...input, shape }, effects);
185
+ // The app stays up only when there is something worth watching.
186
+ if (outcome.ok)
187
+ server.handOver();
188
+ return {
189
+ ...outcome,
190
+ ...(undefined === escalated ? {} : { escalated }),
191
+ ...(undefined === lastDrive?.turns ? {} : { driveTurns: lastDrive.turns }),
192
+ ...(undefined === lastDrive?.costUsd ? {} : { driveCostUsd: lastDrive.costUsd }),
193
+ };
194
+ }
195
+ finally {
196
+ releaseSignals();
197
+ server.stop();
198
+ }
199
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The contract between the agent and the script.
3
+ *
4
+ * Everything setup does is deterministic except for the handful of things that require reading a
5
+ * repository and a human's request and understanding both. Those are exactly what an agent is good
6
+ * at and code is not, so they arrive here as ARGUMENTS rather than as steps somebody walks through:
7
+ * the agent decides, the script executes, and neither does the other's job.
8
+ *
9
+ * What only an agent can know, and therefore what belongs in this type:
10
+ *
11
+ * --flow which journey proves the thing the user cares about. Code can enumerate buttons; it
12
+ * cannot know that checkout matters and the theme toggle does not. Measured, naming
13
+ * it took one app's drive from a ten-minute timeout to 138 seconds, because the turns
14
+ * go into FINDING a flow and naming one removes that search.
15
+ * --app which app in a monorepo the user meant. init can find the ones that are servable;
16
+ * only the request says which of them is being worked on.
17
+ * --env what the app needs to get past its own front door. A key in .env.example, a mock
18
+ * backend named in the README, the variable that skips an auth wall: all readable by
19
+ * an agent, none inferable by a script. Without this a real app sits on a login
20
+ * screen and there is no flow to drive at all.
21
+ * --dev-cmd the command, when the project's own scripts do not say it plainly.
22
+ * --url the app is already running here, so do not start anything.
23
+ *
24
+ * Everything else on this type is an operator's dial, not an agent's judgement.
25
+ */
26
+ /** The bridge port. NOT the dev server's, and conflating them is a documented setup failure. */
27
+ export declare const DEFAULT_BRIDGE_PORT = 4400;
28
+ /** Per-phase budget. The drive gets its own, much larger one. */
29
+ export declare const DEFAULT_PHASE_TIMEOUT_MS = 120000;
30
+ /** What the drive may spend before it is stopped. */
31
+ export declare const DEFAULT_DRIVE_BUDGET_USD = 3;
32
+ export interface SetupOptions {
33
+ /** The journey to drive, in the caller's own words. */
34
+ readonly flow?: string | undefined;
35
+ /** Which app in a monorepo. */
36
+ readonly app?: string | undefined;
37
+ /** Environment the app needs in order to reach a usable state. */
38
+ readonly env?: Readonly<Record<string, string>> | undefined;
39
+ /** The dev command, when the project's scripts do not say it plainly. */
40
+ readonly devCommand?: string | undefined;
41
+ /** The app is already served here. */
42
+ readonly url?: string | undefined;
43
+ readonly bridgePort: number;
44
+ readonly phaseTimeoutMs: number;
45
+ readonly driveBudgetUsd: number;
46
+ readonly driveModel?: string | undefined;
47
+ /** Register the MCP with the other coding agents on this machine. */
48
+ readonly registerAgents: boolean;
49
+ /** Open a browser. False for CI, a headless box, or a tab the user already has. */
50
+ readonly openBrowser: boolean;
51
+ /** Drive a flow. False leaves step five to the caller, and is the only honest way to skip it. */
52
+ readonly drive: boolean;
53
+ /** Re-record with the stronger model when the saved flow is graded weaker than `asserted`. */
54
+ readonly escalateWeakFlow: boolean;
55
+ /** Restart a dev server whose bundle predates the build-config edit. */
56
+ readonly restartStaleDevServer: boolean;
57
+ /** Write files and stop, which is what `init` did before it grew the runtime phases. */
58
+ readonly filesOnly: boolean;
59
+ /** A license key, written to .env and never echoed. */
60
+ readonly licenseKey?: string | undefined;
61
+ }
62
+ export declare const DEFAULT_SETUP_OPTIONS: SetupOptions;
63
+ /** One `KEY=VALUE`, as an agent would pass it. Everything after the first `=` is the value. */
64
+ export declare function parseEnvAssignment(pair: string): {
65
+ key: string;
66
+ value: string;
67
+ } | null;
68
+ /**
69
+ * Fold repeated `--env KEY=VALUE` into one map, ignoring what cannot be a variable.
70
+ *
71
+ * A malformed pair is dropped rather than guessed at: an agent that meant `--env` and typed
72
+ * something else should see its variable missing, not see setup invent one.
73
+ */
74
+ export declare function collectEnv(pairs: readonly string[]): Record<string, string>;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The contract between the agent and the script.
3
+ *
4
+ * Everything setup does is deterministic except for the handful of things that require reading a
5
+ * repository and a human's request and understanding both. Those are exactly what an agent is good
6
+ * at and code is not, so they arrive here as ARGUMENTS rather than as steps somebody walks through:
7
+ * the agent decides, the script executes, and neither does the other's job.
8
+ *
9
+ * What only an agent can know, and therefore what belongs in this type:
10
+ *
11
+ * --flow which journey proves the thing the user cares about. Code can enumerate buttons; it
12
+ * cannot know that checkout matters and the theme toggle does not. Measured, naming
13
+ * it took one app's drive from a ten-minute timeout to 138 seconds, because the turns
14
+ * go into FINDING a flow and naming one removes that search.
15
+ * --app which app in a monorepo the user meant. init can find the ones that are servable;
16
+ * only the request says which of them is being worked on.
17
+ * --env what the app needs to get past its own front door. A key in .env.example, a mock
18
+ * backend named in the README, the variable that skips an auth wall: all readable by
19
+ * an agent, none inferable by a script. Without this a real app sits on a login
20
+ * screen and there is no flow to drive at all.
21
+ * --dev-cmd the command, when the project's own scripts do not say it plainly.
22
+ * --url the app is already running here, so do not start anything.
23
+ *
24
+ * Everything else on this type is an operator's dial, not an agent's judgement.
25
+ */
26
+ /** The bridge port. NOT the dev server's, and conflating them is a documented setup failure. */
27
+ export const DEFAULT_BRIDGE_PORT = 4400;
28
+ /** Per-phase budget. The drive gets its own, much larger one. */
29
+ export const DEFAULT_PHASE_TIMEOUT_MS = 120_000;
30
+ /** What the drive may spend before it is stopped. */
31
+ export const DEFAULT_DRIVE_BUDGET_USD = 3;
32
+ export const DEFAULT_SETUP_OPTIONS = {
33
+ bridgePort: DEFAULT_BRIDGE_PORT,
34
+ phaseTimeoutMs: DEFAULT_PHASE_TIMEOUT_MS,
35
+ driveBudgetUsd: DEFAULT_DRIVE_BUDGET_USD,
36
+ registerAgents: true,
37
+ openBrowser: true,
38
+ drive: true,
39
+ escalateWeakFlow: true,
40
+ restartStaleDevServer: true,
41
+ filesOnly: false,
42
+ };
43
+ /** One `KEY=VALUE`, as an agent would pass it. Everything after the first `=` is the value. */
44
+ export function parseEnvAssignment(pair) {
45
+ const at = pair.indexOf('=');
46
+ if (0 >= at)
47
+ return null;
48
+ const key = pair.slice(0, at).trim();
49
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
50
+ return null;
51
+ return { key, value: pair.slice(at + 1) };
52
+ }
53
+ /**
54
+ * Fold repeated `--env KEY=VALUE` into one map, ignoring what cannot be a variable.
55
+ *
56
+ * A malformed pair is dropped rather than guessed at: an agent that meant `--env` and typed
57
+ * something else should see its variable missing, not see setup invent one.
58
+ */
59
+ export function collectEnv(pairs) {
60
+ const env = {};
61
+ for (const pair of pairs) {
62
+ const parsed = parseEnvAssignment(pair);
63
+ if (null !== parsed)
64
+ env[parsed.key] = parsed.value;
65
+ }
66
+ return env;
67
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Does a transcript exist for this Claude Code session.
3
+ *
4
+ * Two roots, because a shared-config install writes elsewhere and a restart that checks only one of
5
+ * them refuses a session that is perfectly real.
6
+ */
7
+ export declare function claudeTranscriptExists(sessionId: string): boolean;
8
+ /**
9
+ * The codex conversation running in `cwd`, when one is.
10
+ *
11
+ * Codex names no session in the environment — unlike Claude Code — so it is recognised from the
12
+ * rollout file it is writing. Not yet wired to a reader here: returning undefined means "no codex
13
+ * session identified", which is the honest answer until one exists, and the decision it feeds says
14
+ * plainly that the client identified no conversation.
15
+ */
16
+ export declare function codexSessionFor(_cwd: string): string | undefined;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Which conversation is asking, and does it really exist.
3
+ *
4
+ * Both answers come off the filesystem, so they are kept away from the pure decision in relaunch.ts
5
+ * — that is the part worth testing, and it is the part that can be wrong without anybody noticing.
6
+ */
7
+ import { existsSync, readdirSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ /**
11
+ * Does a transcript exist for this Claude Code session.
12
+ *
13
+ * Two roots, because a shared-config install writes elsewhere and a restart that checks only one of
14
+ * them refuses a session that is perfectly real.
15
+ */
16
+ export function claudeTranscriptExists(sessionId) {
17
+ for (const base of [
18
+ join(homedir(), '.claude-shared', 'projects'),
19
+ join(homedir(), '.claude', 'projects'),
20
+ ]) {
21
+ try {
22
+ for (const project of readdirSync(base)) {
23
+ if (existsSync(join(base, project, `${sessionId}.jsonl`)))
24
+ return true;
25
+ }
26
+ }
27
+ catch {
28
+ /* that history directory does not exist on this machine */
29
+ }
30
+ }
31
+ return false;
32
+ }
33
+ /**
34
+ * The codex conversation running in `cwd`, when one is.
35
+ *
36
+ * Codex names no session in the environment — unlike Claude Code — so it is recognised from the
37
+ * rollout file it is writing. Not yet wired to a reader here: returning undefined means "no codex
38
+ * session identified", which is the honest answer until one exists, and the decision it feeds says
39
+ * plainly that the client identified no conversation.
40
+ */
41
+ export function codexSessionFor(_cwd) {
42
+ return undefined;
43
+ }
@@ -24,8 +24,21 @@ interface StatusPayload {
24
24
  * running the command we tell them to run in `init`'s closing line deserves the same sentence.
25
25
  */
26
26
  why?: string;
27
+ /** Port of the verify HTTP endpoint this daemon serves — present only when started with `--http`. */
28
+ verifyPort?: number;
27
29
  }
28
30
  export declare function statusPayload(sessionCount: number, sessions: SessionInfo[],
29
31
  /** The no-session diagnosis, injected so this stays pure and the port probe stays off this path. */
30
- why?: string): StatusPayload;
32
+ why?: string, verifyPort?: number): StatusPayload;
33
+ /**
34
+ * Why `serve --http` cannot claim success against an already-running daemon — or undefined when
35
+ * that daemon already serves the verify HTTP endpoint on the wanted port.
36
+ *
37
+ * `serve` never hands flags to a daemon that is already up, so `--http`/`--http-port` against one
38
+ * used to be accepted, dropped, and reported as success (#687). This is the read side of the
39
+ * `verifyPort` field above: the daemon says which port it serves, and `serve` compares. A daemon
40
+ * too old to report the field — or one that did not answer /status — reads as "not honoured",
41
+ * which errs loud rather than green.
42
+ */
43
+ export declare function verifyEndpointMismatch(status: unknown, wantedPort: number): string | undefined;
31
44
  export {};
@@ -2,7 +2,7 @@ import { CONTRACT_FINGERPRINT } from '@reticlehq/core';
2
2
  import { SERVER_VERSION } from './version/server-version.js';
3
3
  export function statusPayload(sessionCount, sessions,
4
4
  /** The no-session diagnosis, injected so this stays pure and the port probe stays off this path. */
5
- why) {
5
+ why, verifyPort) {
6
6
  return {
7
7
  running: true,
8
8
  version: SERVER_VERSION,
@@ -12,5 +12,27 @@ why) {
12
12
  // Only when there is nothing to explain away: a diagnosis printed beside a live session would
13
13
  // contradict it.
14
14
  ...(0 === sessionCount && why !== undefined ? { why } : {}),
15
+ ...(verifyPort === undefined ? {} : { verifyPort }),
15
16
  };
16
17
  }
18
+ /**
19
+ * Why `serve --http` cannot claim success against an already-running daemon — or undefined when
20
+ * that daemon already serves the verify HTTP endpoint on the wanted port.
21
+ *
22
+ * `serve` never hands flags to a daemon that is already up, so `--http`/`--http-port` against one
23
+ * used to be accepted, dropped, and reported as success (#687). This is the read side of the
24
+ * `verifyPort` field above: the daemon says which port it serves, and `serve` compares. A daemon
25
+ * too old to report the field — or one that did not answer /status — reads as "not honoured",
26
+ * which errs loud rather than green.
27
+ */
28
+ export function verifyEndpointMismatch(status, wantedPort) {
29
+ const served = 'object' === typeof status && status !== null
30
+ ? status['verifyPort']
31
+ : undefined;
32
+ if (served === wantedPort)
33
+ return undefined;
34
+ const fix = 'stop it (`reticle stop`) and run `reticle serve --http` again';
35
+ return 'number' === typeof served
36
+ ? `the daemon already running serves the verify HTTP endpoint on :${String(served)}, not :${String(wantedPort)} — ${fix}`
37
+ : `a daemon is already running without the verify HTTP endpoint — \`--http\` cannot be applied to it; ${fix}`;
38
+ }
@@ -1,5 +1,5 @@
1
1
  import { AutomationHint } from '@reticlehq/core';
2
- export interface AutomationInput {
2
+ interface AutomationInput {
3
3
  env: NodeJS.ProcessEnv;
4
4
  fileExists: (path: string) => boolean;
5
5
  hasTty: boolean;
@@ -15,3 +15,4 @@ export interface AutomationInput {
15
15
  export declare function resolveAutomationHint(input: AutomationInput): AutomationHint | undefined;
16
16
  /** The real reading, taken once per process. */
17
17
  export declare function currentAutomationHint(env: NodeJS.ProcessEnv): AutomationHint | undefined;
18
+ export {};
@@ -53,4 +53,11 @@ export interface DaemonTelemetry {
53
53
  * Report the daemon as started, profile the project once, and keep the session counters flushing.
54
54
  * Every timer is `unref`'d so telemetry can never be the reason a daemon refuses to exit.
55
55
  */
56
- export declare function installDaemonTelemetry(cwd: string, now?: () => number): DaemonTelemetry;
56
+ export declare function installDaemonTelemetry(cwd: string, now?: () => number,
57
+ /**
58
+ * The bridge port this daemon bound, so the profile can say whether an app for this project has
59
+ * EVER connected. Optional: a caller that does not know the port gets a profile without that bit
60
+ * rather than one with a guessed `false`, which is the value the whole field exists to make
61
+ * trustworthy.
62
+ */
63
+ port?: number): DaemonTelemetry;
@@ -10,10 +10,34 @@ import { TelemetryEventKind } from '@reticlehq/core';
10
10
  import { getTelemetry } from './telemetry.js';
11
11
  import { getSessionMetrics } from './session-metrics.js';
12
12
  import { profileProject } from './project-profile.js';
13
- import { startUpdateCheck } from '../update/update-nudge.js';
13
+ import { hasProjectConnectedBefore } from '../session/connection-memory.js';
14
+ import { readProjectId } from '../cli/cli-port.js';
15
+ import { reticleStateHome } from '../daemon/daemon.js';
16
+ import { startUpdateCheck, updateNudgeState } from '../update/update-nudge.js';
14
17
  import { markDaemonStart } from './mcp-connection.js';
15
18
  import { markInstrumentationClock } from './app-instrumented.js';
16
19
  import { markStallClock, STALL_AFTER_MS, stallUptime } from '../session/stall-clock.js';
20
+ /**
21
+ * Whether an app for this project has ever reached Reticle, read from the same durable memory the
22
+ * no-session diagnosis and the server instructions read.
23
+ *
24
+ * Best-effort: an unreadable state home must never be the reason a daemon start emits nothing, and
25
+ * `undefined` here means "not measured" rather than "no". A `false` invented from a read error is
26
+ * exactly the lie this field exists to avoid — it would put the working installs into the cohort we
27
+ * are trying to size.
28
+ */
29
+ function installFacts(cwd, port) {
30
+ if (port === undefined)
31
+ return undefined;
32
+ try {
33
+ return {
34
+ appConnectedBefore: hasProjectConnectedBefore(reticleStateHome(), port, readProjectId(cwd)),
35
+ };
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ }
17
41
  /** Let the daemon finish coming up before walking the source tree for a profile. */
18
42
  const PROJECT_PROFILE_DELAY_MS = 5_000;
19
43
  /**
@@ -44,11 +68,34 @@ export const SESSION_FLUSH_MS = 5 * 60 * 1000;
44
68
  * and 74% of daemons never serve a tool at all.
45
69
  */
46
70
  export const FIRST_FLUSH_MS = 90 * 1000;
71
+ /**
72
+ * Attach what the update nudge did to a summary that is about to be sent.
73
+ *
74
+ * Merged HERE rather than inside `SessionMetrics` deliberately: the nudge's state lives in the
75
+ * update module, and a second copy of it in the metrics object would be one more thing that can
76
+ * drift from the flag the delivery path actually reads. `summarize()` stays a pure roll-up of
77
+ * counters.
78
+ */
79
+ function withNudgeState(summary) {
80
+ const nudge = updateNudgeState();
81
+ return {
82
+ ...summary,
83
+ updateNudged: nudge.shown,
84
+ ...(nudge.offered === undefined ? {} : { updateOffered: nudge.offered }),
85
+ };
86
+ }
47
87
  /**
48
88
  * Report the daemon as started, profile the project once, and keep the session counters flushing.
49
89
  * Every timer is `unref`'d so telemetry can never be the reason a daemon refuses to exit.
50
90
  */
51
- export function installDaemonTelemetry(cwd, now = () => Date.now()) {
91
+ export function installDaemonTelemetry(cwd, now = () => Date.now(),
92
+ /**
93
+ * The bridge port this daemon bound, so the profile can say whether an app for this project has
94
+ * EVER connected. Optional: a caller that does not know the port gets a profile without that bit
95
+ * rather than one with a guessed `false`, which is the value the whole field exists to make
96
+ * trustworthy.
97
+ */
98
+ port) {
52
99
  const metrics = getSessionMetrics(now);
53
100
  void getTelemetry().emit(TelemetryEventKind.DAEMON_STARTED);
54
101
  // Ask npm whether a newer Reticle exists, so the agent can tell the human. Nothing checked before
@@ -66,7 +113,7 @@ export function installDaemonTelemetry(cwd, now = () => Date.now()) {
66
113
  setTimeout(() => {
67
114
  try {
68
115
  void getTelemetry().emit(TelemetryEventKind.PROJECT_PROFILED, {
69
- project: profileProject(cwd, now()),
116
+ project: profileProject(cwd, now(), installFacts(cwd, port)),
70
117
  });
71
118
  }
72
119
  catch {
@@ -93,7 +140,7 @@ export function installDaemonTelemetry(cwd, now = () => Date.now()) {
93
140
  return; // an idle window is not worth an event
94
141
  // NOT daemon_stopped: the daemon is still running. See SESSION_PROGRESS.
95
142
  void getTelemetry().emit(TelemetryEventKind.SESSION_PROGRESS, {
96
- session: metrics.summarize(false),
143
+ session: withNudgeState(metrics.summarize(false)),
97
144
  });
98
145
  metrics.reset();
99
146
  };
@@ -114,7 +161,7 @@ export function installDaemonTelemetry(cwd, now = () => Date.now()) {
114
161
  // The rich one: the whole session in a single event — duration, the tool histogram, error
115
162
  // shapes, verifications, browser launches. This is what replaced the per-tool-call event.
116
163
  await getTelemetry().emit(TelemetryEventKind.DAEMON_STOPPED, {
117
- session: metrics.summarize(true, exit),
164
+ session: withNudgeState(metrics.summarize(true, exit)),
118
165
  });
119
166
  })();
120
167
  return stopped;
@@ -30,7 +30,7 @@ export const ErrorClass = {
30
30
  const PATTERNS = [
31
31
  [/confirmdangerous|destructive action blocked/i, ErrorClass.REFUSAL],
32
32
  [
33
- /\brefus(e|ed|es|ing)\b|not supported|unsupported|cannot .* a <|contenteditable/i,
33
+ /\brefus(e|ed|es|ing)\b|not supported|unsupported|cannot .* a <|contenteditable|cannot hover/i,
34
34
  ErrorClass.REFUSAL,
35
35
  ],
36
36
  [
@@ -1,10 +1,11 @@
1
- import { McpScope, type Feedback } from '@reticlehq/core';
1
+ import { McpScope, StackUnknownReason, type Feedback } from '@reticlehq/core';
2
2
  /** The context fields — the whole `Feedback` shape minus what the author supplies. */
3
3
  export type FeedbackContext = Pick<Feedback, 'stack' | 'stackMajor' | 'runtime' | 'engine' | 'driver' | 'client' | 'clientVersion' | 'mcpScope'>;
4
4
  export declare function detectStack(cwd: string, read?: (path: string) => string): {
5
5
  stack?: string;
6
6
  stackMajor?: number;
7
7
  stackSource?: 'cwd' | 'workspace';
8
+ stackUnknownReason?: StackUnknownReason;
8
9
  };
9
10
  /**
10
11
  * User scope unless the project checks in its own MCP registration. `reticle init` deliberately
@@ -12,6 +13,13 @@ export declare function detectStack(cwd: string, read?: (path: string) => string
12
13
  * itself the interesting signal.
13
14
  */
14
15
  export declare function detectMcpScope(cwd: string, exists?: (path: string) => boolean): McpScope;
16
+ /**
17
+ * The MCP client on the other end, self-reported in its `initialize` handshake (`claude-code`,
18
+ * `cursor-vscode`, …). Read through a HOOK rather than sniffing env vars: the handshake is the
19
+ * client's own claim about itself, while an env-var table is a guess that silently rots every time a
20
+ * vendor renames a variable. Unset (a CLI run with no MCP peer) simply reports no client.
21
+ */
22
+ /** Kept as the telemetry-facing name; the mechanism itself lives in mcp/client-identity.ts. */
15
23
  export declare function setMcpClientNameHook(hook: () => {
16
24
  name?: string;
17
25
  version?: string;