@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,148 @@
1
+ /**
2
+ * Find an enterprise licence key the customer actually placed, wherever they reasonably placed it.
3
+ *
4
+ * The daemon folded in `<cwd>/.env` and nothing else. It is spawned without an explicit `cwd`, so it
5
+ * inherits whatever directory the editor launched the MCP server from — in a monorepo that is the
6
+ * workspace root while the key sits in the app's own `.env`, and under some editors it is the user's
7
+ * home. The key was never read, `describeLicense` reported `missing`, and every event that customer
8
+ * produced said they had no licence, which is indistinguishable from a customer who has none.
9
+ *
10
+ * That is the worst shape a licensing bug can take. A key that fails LOUDLY gets reported by the
11
+ * customer within the hour. A key that silently fails to register leaves them believing they are
12
+ * licensed and us believing they are not, and neither side finds out.
13
+ *
14
+ * Two directions, because the field hits both: DOWN one level into app directories (the daemon
15
+ * starts at the workspace root) and UP to the project root (the editor starts inside the app).
16
+ *
17
+ * ONLY the licence key is ever taken out of a file found this way, and that restriction is the point
18
+ * rather than an optimisation. Bulk-importing a `.env` discovered by walking would let a directory
19
+ * the caller never named rebind the daemon's port, its telemetry gate or its allowed origins — a far
20
+ * worse bug than the one this fixes. `loadDotEnv` still owns the local file, where importing
21
+ * everything is what the caller asked for.
22
+ */
23
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
24
+ import { dirname, join } from 'node:path';
25
+ import { LICENSE_KEY_ENV } from './license.js';
26
+ import { discoverProjectConfigs } from '../cli/config-discovery.js';
27
+ /** The files a framework tells people to put secrets in, most specific first. */
28
+ const ENV_FILES = ['.env.local', '.env'];
29
+ /** Stop the upward walk here: a licence belongs to a project, not to a machine. */
30
+ const ROOT_MARKERS = ['.git', 'package.json', 'pnpm-workspace.yaml'];
31
+ /** A bound on the upward walk, so a pathological path cannot spin. */
32
+ const MAX_DEPTH = 12;
33
+ function readKey(file) {
34
+ try {
35
+ if (!existsSync(file))
36
+ return undefined;
37
+ for (const raw of readFileSync(file, 'utf8').split('\n')) {
38
+ const line = raw.trim();
39
+ if (0 === line.length || line.startsWith('#'))
40
+ continue;
41
+ const eq = line.indexOf('=');
42
+ if (eq <= 0)
43
+ continue;
44
+ if (line.slice(0, eq).trim() !== LICENSE_KEY_ENV)
45
+ continue;
46
+ const value = line.slice(eq + 1).trim();
47
+ const first = value[0];
48
+ const unquoted = ('"' === first || "'" === first) && value.endsWith(first) && value.length >= 2
49
+ ? value.slice(1, -1)
50
+ : value;
51
+ if (unquoted.length > 0)
52
+ return unquoted;
53
+ }
54
+ }
55
+ catch {
56
+ // A licence probe may never take the daemon down, and an unreadable file is not an error here.
57
+ }
58
+ return undefined;
59
+ }
60
+ function inDir(dir) {
61
+ for (const name of ENV_FILES) {
62
+ const found = readKey(join(dir, name));
63
+ if (found !== undefined)
64
+ return found;
65
+ }
66
+ return undefined;
67
+ }
68
+ function isRoot(dir) {
69
+ return ROOT_MARKERS.some((marker) => existsSync(join(dir, marker)));
70
+ }
71
+ /**
72
+ * The directories that hold a `.reticle.json`: the apps that were actually instrumented.
73
+ *
74
+ * This is the closest thing the daemon has to "the session's project directory", and it is the same
75
+ * discovery the no-session diagnosis uses to tell an agent where the app really is. Reusing it means
76
+ * the licence search and that diagnosis can never disagree about where the project lives.
77
+ *
78
+ * It replaced a guess at `apps/*` and `packages/*`, which missed exactly the repo shape
79
+ * `findWorkspaceApps` was written for: three apps at `web/`, `admin/` and `space/`.
80
+ */
81
+ /**
82
+ * The conventional layout, kept as a fallback beside the declaration-driven search above.
83
+ *
84
+ * `discoverProjectConfigs` descends only into DECLARED workspaces, which is right and is what a real
85
+ * monorepo has. But `apps/web` with no `workspaces` entry is a shape people genuinely ship, and for
86
+ * a licence key the cost of one extra `readdir` is nothing against reporting a paying customer as
87
+ * unlicensed. Strictly widens coverage; it can never contradict the discovery, only add to it.
88
+ */
89
+ const CONVENTIONAL_DIRS = ['apps', 'packages'];
90
+ function inConventionalChildren(dir) {
91
+ for (const group of CONVENTIONAL_DIRS) {
92
+ const base = join(dir, group);
93
+ let entries;
94
+ try {
95
+ if (!existsSync(base) || !statSync(base).isDirectory())
96
+ continue;
97
+ entries = readdirSync(base);
98
+ }
99
+ catch {
100
+ continue;
101
+ }
102
+ for (const entry of entries) {
103
+ const found = inDir(join(base, entry));
104
+ if (found !== undefined)
105
+ return found;
106
+ }
107
+ }
108
+ return undefined;
109
+ }
110
+ function inDiscoveredProjects(dir) {
111
+ let found;
112
+ try {
113
+ found = discoverProjectConfigs(dir).found;
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ for (const config of found) {
119
+ const key = inDir(config.directory);
120
+ if (key !== undefined)
121
+ return key;
122
+ }
123
+ return undefined;
124
+ }
125
+ /**
126
+ * The key, or undefined. Never throws, never mutates anything the caller did not ask it to.
127
+ *
128
+ * A key already present in `env` always wins: that is the operator stating it explicitly, and a file
129
+ * silently overriding it is the surprise `loadDotEnv` already refuses to cause.
130
+ */
131
+ export function licenseKeyFromEnvFiles(startDir, env = process.env) {
132
+ const existing = env[LICENSE_KEY_ENV];
133
+ if (existing !== undefined && existing.length > 0)
134
+ return existing;
135
+ let dir = startDir;
136
+ for (let depth = 0; depth < MAX_DEPTH; depth++) {
137
+ const here = inDir(dir) ?? inDiscoveredProjects(dir) ?? inConventionalChildren(dir);
138
+ if (here !== undefined)
139
+ return here;
140
+ if (isRoot(dir))
141
+ break;
142
+ const parent = dirname(dir);
143
+ if (parent === dir)
144
+ break;
145
+ dir = parent;
146
+ }
147
+ return undefined;
148
+ }
@@ -0,0 +1,31 @@
1
+ import * as http from 'node:http';
2
+ /**
3
+ * The one HTTP agent every loopback call in this process goes through.
4
+ *
5
+ * Without an `agent:`, Node hands the request to `http.globalAgent`, whose idle timeout is 5
6
+ * seconds. The MCP proxy's POST leg is one request per JSON-RPC message and an agent thinks for
7
+ * longer than five seconds between tool calls essentially always — so every single tool call opened
8
+ * a fresh TCP socket. On Windows that is the ephemeral-port / non-paged-pool exhaustion pattern, and
9
+ * the `ENOBUFS` it eventually raises killed the call outright.
10
+ *
11
+ * The numbers, each one a decision and not a taste:
12
+ *
13
+ * - `keepAlive: true` — the whole point: hold the socket open between calls so there is no churn to
14
+ * exhaust anything with.
15
+ * - `timeout: LOOPBACK_IDLE_MS` (45s) — how long an idle socket is kept for reuse. It has to outlast
16
+ * a model's think-time between two tool calls (which is what 5s failed to do) and it has to stay
17
+ * BELOW the daemon's own keep-alive timeout, so that the client is always the side that retires a
18
+ * socket. If the server closed first we would keep picking up sockets it had already closed, and
19
+ * that failure looks exactly like the one case we refuse to retry (see `isRetryableConnectError`).
20
+ * The daemon's side of that pact is `HTTP_KEEP_ALIVE_TIMEOUT_MS` in `http-server.ts`.
21
+ * - `keepAliveMsecs: 10_000` — TCP-level keep-alive probes on an idle socket, so a daemon that died
22
+ * without a FIN is noticed rather than handed back out as a live connection.
23
+ * - `maxSockets: 8` — the proxy multiplexes ONE MCP client, whose calls are near enough serial; 8 is
24
+ * generous headroom for overlapping notifications while capping the worst case far below anything
25
+ * a port table would notice. Exceeding it queues rather than fails.
26
+ */
27
+ export declare const LOOPBACK_IDLE_MS = 45000;
28
+ /** Named separately because `http.Agent` does not expose its options back to a caller (or a test). */
29
+ export declare const LOOPBACK_AGENT_OPTIONS: http.AgentOptions;
30
+ export declare const loopbackAgent: http.Agent;
31
+ export declare function isRetryableConnectError(err: unknown): boolean;
@@ -0,0 +1,57 @@
1
+ import * as http from 'node:http';
2
+ /**
3
+ * The one HTTP agent every loopback call in this process goes through.
4
+ *
5
+ * Without an `agent:`, Node hands the request to `http.globalAgent`, whose idle timeout is 5
6
+ * seconds. The MCP proxy's POST leg is one request per JSON-RPC message and an agent thinks for
7
+ * longer than five seconds between tool calls essentially always — so every single tool call opened
8
+ * a fresh TCP socket. On Windows that is the ephemeral-port / non-paged-pool exhaustion pattern, and
9
+ * the `ENOBUFS` it eventually raises killed the call outright.
10
+ *
11
+ * The numbers, each one a decision and not a taste:
12
+ *
13
+ * - `keepAlive: true` — the whole point: hold the socket open between calls so there is no churn to
14
+ * exhaust anything with.
15
+ * - `timeout: LOOPBACK_IDLE_MS` (45s) — how long an idle socket is kept for reuse. It has to outlast
16
+ * a model's think-time between two tool calls (which is what 5s failed to do) and it has to stay
17
+ * BELOW the daemon's own keep-alive timeout, so that the client is always the side that retires a
18
+ * socket. If the server closed first we would keep picking up sockets it had already closed, and
19
+ * that failure looks exactly like the one case we refuse to retry (see `isRetryableConnectError`).
20
+ * The daemon's side of that pact is `HTTP_KEEP_ALIVE_TIMEOUT_MS` in `http-server.ts`.
21
+ * - `keepAliveMsecs: 10_000` — TCP-level keep-alive probes on an idle socket, so a daemon that died
22
+ * without a FIN is noticed rather than handed back out as a live connection.
23
+ * - `maxSockets: 8` — the proxy multiplexes ONE MCP client, whose calls are near enough serial; 8 is
24
+ * generous headroom for overlapping notifications while capping the worst case far below anything
25
+ * a port table would notice. Exceeding it queues rather than fails.
26
+ */
27
+ export const LOOPBACK_IDLE_MS = 45_000;
28
+ /** Named separately because `http.Agent` does not expose its options back to a caller (or a test). */
29
+ export const LOOPBACK_AGENT_OPTIONS = {
30
+ keepAlive: true,
31
+ keepAliveMsecs: 10_000,
32
+ timeout: LOOPBACK_IDLE_MS,
33
+ maxSockets: 8,
34
+ };
35
+ export const loopbackAgent = new http.Agent(LOOPBACK_AGENT_OPTIONS);
36
+ /**
37
+ * Socket-level failures worth another attempt — every one of them a statement about this machine's
38
+ * networking stack, not about the request.
39
+ *
40
+ * The list is deliberately narrow. It is not the retry decision on its own: the caller must ALSO
41
+ * know the request never reached the daemon. What the code check buys is that a programming error
42
+ * (`ERR_INVALID_URL`, a bad argument) cannot be retried — those fail before a socket too, and would
43
+ * otherwise burn the whole attempt budget on a bug that will never succeed.
44
+ */
45
+ const RETRYABLE_CODES = new Set([
46
+ 'ENOBUFS', // Windows: non-paged pool / ephemeral port exhaustion — the reported failure
47
+ 'EADDRNOTAVAIL', // no ephemeral port left to bind
48
+ 'EMFILE', // per-process fd limit
49
+ 'ENFILE', // system-wide fd limit
50
+ 'ECONNREFUSED', // daemon restarting, listener not back yet
51
+ 'ECONNRESET', // peer closed the connection
52
+ 'ETIMEDOUT', // connect never completed
53
+ ]);
54
+ export function isRetryableConnectError(err) {
55
+ const code = err?.code;
56
+ return 'string' === typeof code && RETRYABLE_CODES.has(code);
57
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Two durable bits about the CLIENT side of the install: has an MCP client ever started Reticle's
3
+ * MCP server on this port, and has one ever asked it for the tool list.
4
+ *
5
+ * Some hosts register the server, start it, and then never send `tools/list` until the client is
6
+ * restarted. The agent has no Reticle tools, the user has no message, and from our side that install
7
+ * is byte-identical to one somebody wired and abandoned: daemon idle, no sessions, nothing to say.
8
+ * `status` could report "registered" and "connected"; it could not report the state in between,
9
+ * which is the one with an answer ("restart your client").
10
+ *
11
+ * Both bits record something that HAPPENED — the proxy ran, a `tools/list` arrived — never an
12
+ * inference from a config file. A config entry proves somebody wrote a line of JSON; it does not
13
+ * prove a client ever read it, which is precisely the thing in question here.
14
+ *
15
+ * Deliberately the same shape as `connection-memory.ts`: one small JSON file per port under the
16
+ * daemon's state home, best-effort in both directions. It is a HINT, so every failure degrades to
17
+ * "we do not know" rather than throwing — a diagnostic that can crash the process it is diagnosing
18
+ * is worse than no diagnostic at all.
19
+ *
20
+ * KNOWN LIMIT, and why the messages below say so out loud: the record is per PORT, not per client.
21
+ * Two clients on one machine share it, so a healthy one masks a broken one, and the record itself
22
+ * only begins on the version that introduced it — a long-working install reads as never attached
23
+ * until its client next starts the server. Both limits are stated in the text the user reads,
24
+ * because an honest "here is what I can see, here is what to check" beats a confident wrong no.
25
+ */
26
+ /** What the client side of this install has been observed doing on this port. */
27
+ export declare const AttachState: {
28
+ /** No client has started Reticle's MCP server here, as far as this record goes. */
29
+ readonly NEVER_ATTACHED: "never-attached";
30
+ /** A client started it and never asked for the tool list — the state that needs a restart. */
31
+ readonly NEVER_ENUMERATED: "never-enumerated";
32
+ /**
33
+ * The catalogue was read and no tool was ever called: both ends healthy, nothing crossing.
34
+ *
35
+ * Reported from the field as the hardest state to diagnose, and it is the one every component
36
+ * check passes. The browser SDK is injected, the daemon has a live session, the client lists all
37
+ * the tools, and the agent still reports Reticle as absent. `enumerated` used to cover this, which
38
+ * meant the most-broken install we know of read identically to a perfect one.
39
+ */
40
+ readonly NEVER_CALLED: "never-called";
41
+ /** Started, enumerated, and actually used. Nothing to say. */
42
+ readonly ENUMERATED: "enumerated";
43
+ };
44
+ export type AttachState = (typeof AttachState)[keyof typeof AttachState];
45
+ /** Record that an MCP client started the proxy on this port. */
46
+ export declare function rememberProxyStarted(stateDir: string, port: number): void;
47
+ /**
48
+ * Record that a client actually CALLED a tool here, not merely listed them.
49
+ *
50
+ * Written once and then skipped, like the other two: this runs on the hot path of every tool call,
51
+ * and a diagnostic that writes a file per call would cost more than it tells anybody.
52
+ */
53
+ export declare function rememberToolCalled(stateDir: string, port: number): void;
54
+ /**
55
+ * Record that a client asked for the tool list on this port.
56
+ *
57
+ * Enumeration implies attachment — a `tools/list` cannot arrive at a server nobody started — so this
58
+ * sets both bits, and a record written by an older proxy that only ever set one is still read
59
+ * correctly.
60
+ */
61
+ export declare function rememberEnumerated(stateDir: string, port: number): void;
62
+ /** Which of the four client-side states this port is in. */
63
+ export declare function attachState(stateDir: string, port: number): AttachState;
64
+ /**
65
+ * The sentence to print for a state, or `undefined` when there is nothing to fix.
66
+ *
67
+ * The healthy case has to stay silent: advice printed beside a working client reads as though
68
+ * something is still wrong, which is its own kind of lie.
69
+ */
70
+ export declare function describeAttachState(state: AttachState): string | undefined;
71
+ /** What `reticle status` reports about the client side of the install. */
72
+ export interface AttachStatusFields {
73
+ mcpClient: AttachState;
74
+ mcpClientAction?: string;
75
+ }
76
+ /**
77
+ * The client-side fields for one `status` run.
78
+ *
79
+ * The state is reported on EVERY run, healthy included — "the client has read the tools" is the
80
+ * fact that was missing, and reporting it only when broken leaves the reader unable to tell a
81
+ * healthy client from a version too old to look. The advice rides along only when there is any.
82
+ */
83
+ export declare function attachStatusFields(stateDir: string, port: number): AttachStatusFields;
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Two durable bits about the CLIENT side of the install: has an MCP client ever started Reticle's
3
+ * MCP server on this port, and has one ever asked it for the tool list.
4
+ *
5
+ * Some hosts register the server, start it, and then never send `tools/list` until the client is
6
+ * restarted. The agent has no Reticle tools, the user has no message, and from our side that install
7
+ * is byte-identical to one somebody wired and abandoned: daemon idle, no sessions, nothing to say.
8
+ * `status` could report "registered" and "connected"; it could not report the state in between,
9
+ * which is the one with an answer ("restart your client").
10
+ *
11
+ * Both bits record something that HAPPENED — the proxy ran, a `tools/list` arrived — never an
12
+ * inference from a config file. A config entry proves somebody wrote a line of JSON; it does not
13
+ * prove a client ever read it, which is precisely the thing in question here.
14
+ *
15
+ * Deliberately the same shape as `connection-memory.ts`: one small JSON file per port under the
16
+ * daemon's state home, best-effort in both directions. It is a HINT, so every failure degrades to
17
+ * "we do not know" rather than throwing — a diagnostic that can crash the process it is diagnosing
18
+ * is worse than no diagnostic at all.
19
+ *
20
+ * KNOWN LIMIT, and why the messages below say so out loud: the record is per PORT, not per client.
21
+ * Two clients on one machine share it, so a healthy one masks a broken one, and the record itself
22
+ * only begins on the version that introduced it — a long-working install reads as never attached
23
+ * until its client next starts the server. Both limits are stated in the text the user reads,
24
+ * because an honest "here is what I can see, here is what to check" beats a confident wrong no.
25
+ */
26
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
27
+ import { join } from 'node:path';
28
+ /** What the client side of this install has been observed doing on this port. */
29
+ export const AttachState = {
30
+ /** No client has started Reticle's MCP server here, as far as this record goes. */
31
+ NEVER_ATTACHED: 'never-attached',
32
+ /** A client started it and never asked for the tool list — the state that needs a restart. */
33
+ NEVER_ENUMERATED: 'never-enumerated',
34
+ /**
35
+ * The catalogue was read and no tool was ever called: both ends healthy, nothing crossing.
36
+ *
37
+ * Reported from the field as the hardest state to diagnose, and it is the one every component
38
+ * check passes. The browser SDK is injected, the daemon has a live session, the client lists all
39
+ * the tools, and the agent still reports Reticle as absent. `enumerated` used to cover this, which
40
+ * meant the most-broken install we know of read identically to a perfect one.
41
+ */
42
+ NEVER_CALLED: 'never-called',
43
+ /** Started, enumerated, and actually used. Nothing to say. */
44
+ ENUMERATED: 'enumerated',
45
+ };
46
+ /**
47
+ * What to do about a client that started Reticle and never listed its tools.
48
+ *
49
+ * Names the cause AND the action, like the rest of the doctor/status vocabulary, and names its own
50
+ * blind spot: this record cannot see WHICH client asked, so it must not tell somebody running two
51
+ * of them that the one in front of them is the broken one.
52
+ */
53
+ const NEVER_ENUMERATED_ACTION = 'an MCP client started Reticle on this port and never asked for the tool list, so the tools are ' +
54
+ 'here and your client has not read them — restart your MCP client, which is what makes it list ' +
55
+ 'them again. This record is per port, not per client, so with more than one client registered ' +
56
+ 'the one that never listed may not be the one you are looking at.';
57
+ /**
58
+ * What to do when nothing has ever attached.
59
+ *
60
+ * The hedge is not padding. This record begins the first time a client starts the server on a
61
+ * version that keeps it, so an install that has worked for months reads as never attached until its
62
+ * client next starts Reticle — and telling that user their server is unregistered would send them
63
+ * to re-run `init` over a working setup.
64
+ */
65
+ /**
66
+ * The link that is broken, named as a link rather than as a component.
67
+ *
68
+ * Every other check in this product is about a PIECE: is the daemon up, is the SDK injected, is a
69
+ * session live, are the tools registered. This state is the one where every piece passes and the
70
+ * chain still does not carry anything, so the message has to say which hop failed and what to do
71
+ * about it, not repeat that the pieces are fine.
72
+ */
73
+ const NEVER_CALLED_ACTION = "an MCP client listed Reticle's tools on this port and has never called one, so the tools are " +
74
+ 'registered and no request has reached the daemon. That is the agent-to-daemon hop, not the ' +
75
+ 'browser or the daemon: both of those can look healthy while this one is broken. Ask your agent ' +
76
+ 'to run `reticle_sessions` explicitly. If it answers, the link is fine and the agent simply had ' +
77
+ 'no reason to call it; if it says Reticle is unavailable, the client is talking to a different ' +
78
+ 'server than the one this record belongs to, so re-check which command and port it launches. ' +
79
+ 'This record is per port, not per client.';
80
+ const NEVER_ATTACHED_ACTION = 'no MCP client has started Reticle on this port, so either the server is not registered with your ' +
81
+ 'client or nothing has attached since this record began — register it with `npx ' +
82
+ '@reticlehq/server init`, then restart your MCP client and run status again.';
83
+ const EMPTY = { attached: false, enumerated: false, called: false };
84
+ function memoryPath(stateDir, port) {
85
+ return join(stateDir, `attach-${String(port)}.json`);
86
+ }
87
+ /** The record for a port. Empty on absent, unreadable, or malformed state. */
88
+ function read(stateDir, port) {
89
+ try {
90
+ const parsed = JSON.parse(readFileSync(memoryPath(stateDir, port), 'utf8'));
91
+ if ('object' !== typeof parsed || null === parsed)
92
+ return EMPTY;
93
+ const record = parsed;
94
+ return {
95
+ attached: true === record.attached,
96
+ enumerated: true === record.enumerated,
97
+ called: true === record.called,
98
+ };
99
+ }
100
+ catch {
101
+ return EMPTY;
102
+ }
103
+ }
104
+ /** Write the record back, best-effort. A hint must never be the reason a proxy fails to serve. */
105
+ function write(stateDir, port, record) {
106
+ try {
107
+ mkdirSync(stateDir, { recursive: true });
108
+ writeFileSync(memoryPath(stateDir, port), JSON.stringify(record), 'utf8');
109
+ }
110
+ catch {
111
+ // An unwritable state home is already reported elsewhere; it must not break the client path.
112
+ }
113
+ }
114
+ /** Record that an MCP client started the proxy on this port. */
115
+ export function rememberProxyStarted(stateDir, port) {
116
+ const known = read(stateDir, port);
117
+ if (known.attached)
118
+ return;
119
+ write(stateDir, port, { ...known, attached: true });
120
+ }
121
+ /**
122
+ * Record that a client actually CALLED a tool here, not merely listed them.
123
+ *
124
+ * Written once and then skipped, like the other two: this runs on the hot path of every tool call,
125
+ * and a diagnostic that writes a file per call would cost more than it tells anybody.
126
+ */
127
+ export function rememberToolCalled(stateDir, port) {
128
+ const known = read(stateDir, port);
129
+ if (known.called)
130
+ return;
131
+ write(stateDir, port, { ...known, called: true });
132
+ }
133
+ /**
134
+ * Record that a client asked for the tool list on this port.
135
+ *
136
+ * Enumeration implies attachment — a `tools/list` cannot arrive at a server nobody started — so this
137
+ * sets both bits, and a record written by an older proxy that only ever set one is still read
138
+ * correctly.
139
+ */
140
+ export function rememberEnumerated(stateDir, port) {
141
+ const known = read(stateDir, port);
142
+ if (known.enumerated)
143
+ return;
144
+ write(stateDir, port, { ...known, attached: true, enumerated: true });
145
+ }
146
+ /** Which of the four client-side states this port is in. */
147
+ export function attachState(stateDir, port) {
148
+ const known = read(stateDir, port);
149
+ // Ordered outward along the chain: attached, then listed, then actually used. The last hop is
150
+ // the one every component check misses, so it is asked last and answered explicitly.
151
+ if (!known.attached)
152
+ return AttachState.NEVER_ATTACHED;
153
+ if (!known.enumerated)
154
+ return AttachState.NEVER_ENUMERATED;
155
+ return known.called ? AttachState.ENUMERATED : AttachState.NEVER_CALLED;
156
+ }
157
+ /**
158
+ * The sentence to print for a state, or `undefined` when there is nothing to fix.
159
+ *
160
+ * The healthy case has to stay silent: advice printed beside a working client reads as though
161
+ * something is still wrong, which is its own kind of lie.
162
+ */
163
+ export function describeAttachState(state) {
164
+ if (state === AttachState.NEVER_ENUMERATED)
165
+ return NEVER_ENUMERATED_ACTION;
166
+ if (state === AttachState.NEVER_ATTACHED)
167
+ return NEVER_ATTACHED_ACTION;
168
+ if (state === AttachState.NEVER_CALLED)
169
+ return NEVER_CALLED_ACTION;
170
+ return undefined;
171
+ }
172
+ /**
173
+ * The client-side fields for one `status` run.
174
+ *
175
+ * The state is reported on EVERY run, healthy included — "the client has read the tools" is the
176
+ * fact that was missing, and reporting it only when broken leaves the reader unable to tell a
177
+ * healthy client from a version too old to look. The advice rides along only when there is any.
178
+ */
179
+ export function attachStatusFields(stateDir, port) {
180
+ const state = attachState(stateDir, port);
181
+ const action = describeAttachState(state);
182
+ // OMITTED rather than undefined: `exactOptionalPropertyTypes` is on, and an absent key is also
183
+ // what keeps the healthy line to one fact.
184
+ return { mcpClient: state, ...(action === undefined ? {} : { mcpClientAction: action }) };
185
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * WHICH agent is on the other end of this daemon.
3
+ *
4
+ * MCP clients announce themselves in the `initialize` handshake — `claude-code`, `cursor-vscode`,
5
+ * `windsurf`, and so on. That claim is the ONLY honest source for this: an env-var table
6
+ * (`CLAUDECODE`, `CURSOR_TRACE_ID`, …) is a guess that silently rots every time a vendor renames a
7
+ * variable, and it cannot see a client nobody has added to the table yet.
8
+ *
9
+ * Read through a hook rather than a stored value because the handshake happens after the server is
10
+ * constructed, and a session can reconnect under a different client entirely.
11
+ *
12
+ * Lives here, not in telemetry, because it stopped being a telemetry detail the moment a
13
+ * verification RUN needed to record which agent produced it — and `runs/` must not import
14
+ * `telemetry/` to find out.
15
+ *
16
+ * Note what is NOT available and cannot be: the MODEL. `clientInfo` carries a name, a title and a
17
+ * version; MCP has no concept of a model, so the transport genuinely cannot tell us. Anything
18
+ * claiming to know it would be inventing it.
19
+ */
20
+ export interface McpClientIdentity {
21
+ name?: string;
22
+ version?: string;
23
+ }
24
+ export declare function setMcpClientIdentityHook(next: () => McpClientIdentity | undefined): void;
25
+ /** For tests, and for a daemon that is reused across transports. */
26
+ export declare function clearMcpClientIdentityHook(): void;
27
+ /**
28
+ * The client's own name and version, trimmed and bounded. Empty object when no MCP peer has
29
+ * introduced itself — a plain CLI run has no client, and reporting one would be a fabrication.
30
+ */
31
+ export declare function mcpClientIdentity(): McpClientIdentity;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * WHICH agent is on the other end of this daemon.
3
+ *
4
+ * MCP clients announce themselves in the `initialize` handshake — `claude-code`, `cursor-vscode`,
5
+ * `windsurf`, and so on. That claim is the ONLY honest source for this: an env-var table
6
+ * (`CLAUDECODE`, `CURSOR_TRACE_ID`, …) is a guess that silently rots every time a vendor renames a
7
+ * variable, and it cannot see a client nobody has added to the table yet.
8
+ *
9
+ * Read through a hook rather than a stored value because the handshake happens after the server is
10
+ * constructed, and a session can reconnect under a different client entirely.
11
+ *
12
+ * Lives here, not in telemetry, because it stopped being a telemetry detail the moment a
13
+ * verification RUN needed to record which agent produced it — and `runs/` must not import
14
+ * `telemetry/` to find out.
15
+ *
16
+ * Note what is NOT available and cannot be: the MODEL. `clientInfo` carries a name, a title and a
17
+ * version; MCP has no concept of a model, so the transport genuinely cannot tell us. Anything
18
+ * claiming to know it would be inventing it.
19
+ */
20
+ /** Caps, so a hostile or broken client cannot write unbounded strings into an artifact. */
21
+ const MAX_NAME = 64;
22
+ const MAX_VERSION = 32;
23
+ let hook;
24
+ export function setMcpClientIdentityHook(next) {
25
+ hook = next;
26
+ }
27
+ /** For tests, and for a daemon that is reused across transports. */
28
+ export function clearMcpClientIdentityHook() {
29
+ hook = undefined;
30
+ }
31
+ /**
32
+ * The client's own name and version, trimmed and bounded. Empty object when no MCP peer has
33
+ * introduced itself — a plain CLI run has no client, and reporting one would be a fabrication.
34
+ */
35
+ export function mcpClientIdentity() {
36
+ try {
37
+ const info = hook?.();
38
+ const name = info?.name;
39
+ const version = info?.version;
40
+ return {
41
+ ...(name !== undefined && '' !== name ? { name: name.slice(0, MAX_NAME) } : {}),
42
+ ...(version !== undefined && '' !== version
43
+ ? { version: version.slice(0, MAX_VERSION) }
44
+ : {}),
45
+ };
46
+ }
47
+ catch {
48
+ // A hook that throws must never fail the thing that asked — this is descriptive, not load-bearing.
49
+ return {};
50
+ }
51
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The MCP proxy's POST leg: its own socket pool, and the one retry that is safe to make.
3
+ *
4
+ * Split out of `mcp-proxy.ts` when that file passed the 1000-line backstop after two independent
5
+ * hardenings of this path met in a merge. It earns its own module rather than being an arbitrary
6
+ * slice: everything here is about ONE thing — getting a single JSON-RPC line into the daemon, or
7
+ * saying honestly that it never arrived — and the file it came from is about the SSE stream.
8
+ */
9
+ import * as http from 'node:http';
10
+ /** One bounded pool for the short-lived POST side of each MCP SSE session. */
11
+ export declare const MCP_PROXY_HTTP_AGENT_OPTIONS: {
12
+ readonly keepAlive: true;
13
+ readonly keepAliveMsecs: 30000;
14
+ readonly timeout: 60000;
15
+ readonly maxSockets: 8;
16
+ readonly scheduling: "lifo";
17
+ };
18
+ export declare function shouldRetryUnsentPost(error: unknown, bytesWritten: number, retryCount: number): boolean;
19
+ export interface PostFailure {
20
+ reason: string;
21
+ transport: boolean;
22
+ attempts: number;
23
+ }
24
+ type HttpRequest = (options: http.RequestOptions, callback: (response: http.IncomingMessage) => void) => http.ClientRequest;
25
+ export declare function postToSession(url: string, body: string, request?: HttpRequest): Promise<PostFailure | null>;
26
+ export {};