@reticlehq/server 2.3.0 → 2.5.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 (424) hide show
  1. package/SKILL.md +702 -0
  2. package/dist/bridge/auth-failure-reason.d.ts +19 -0
  3. package/dist/bridge/auth-failure-reason.js +29 -0
  4. package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
  5. package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
  6. package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +105 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
  10. package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
  11. package/dist/capsule/capsule-store.d.ts +8 -3
  12. package/dist/capsule/capsule-store.js +7 -2
  13. package/dist/capsule/capsule.js +3 -3
  14. package/dist/capsule/causal-summary.d.ts +8 -0
  15. package/dist/capsule/causal-summary.js +22 -12
  16. package/dist/capsule/divergence.d.ts +9 -4
  17. package/dist/capsule/divergence.js +8 -8
  18. package/dist/capsule/predicate-to-links.js +11 -8
  19. package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
  20. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  21. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  22. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  23. package/dist/cli/cli-launch.js +171 -0
  24. package/dist/cli/cli-parse.d.ts +143 -0
  25. package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
  26. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
  27. package/dist/{cli-port.js → cli/cli-port.js} +29 -8
  28. package/dist/cli/cli-update-commands.d.ts +4 -0
  29. package/dist/cli/cli-update-commands.js +93 -0
  30. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  31. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  32. package/dist/cli.d.ts +2 -2
  33. package/dist/cli.js +182 -93
  34. package/dist/cloud/cloud-config.d.ts +0 -1
  35. package/dist/cloud/cloud-config.js +9 -9
  36. package/dist/cloud/cloud-sync.d.ts +1 -7
  37. package/dist/cloud/cloud-sync.js +12 -12
  38. package/dist/crawl/crawl-empty.d.ts +16 -0
  39. package/dist/crawl/crawl-empty.js +24 -0
  40. package/dist/crawl/crawl-tools.js +4 -1
  41. package/dist/crawl/crawl.js +26 -7
  42. package/dist/daemon/daemon-resilience.d.ts +49 -0
  43. package/dist/daemon/daemon-resilience.js +191 -0
  44. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  45. package/dist/daemon/daemon-usefulness.js +62 -0
  46. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  47. package/dist/{daemon.js → daemon/daemon.js} +80 -48
  48. package/dist/daemon/idle-grace.d.ts +32 -0
  49. package/dist/daemon/idle-grace.js +41 -0
  50. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
  51. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
  52. package/dist/domain/domain-model.js +2 -2
  53. package/dist/events/body-failures.js +8 -8
  54. package/dist/events/contradictions.d.ts +1 -1
  55. package/dist/events/contradictions.js +60 -10
  56. package/dist/events/echo-mismatch.js +10 -10
  57. package/dist/events/event-filters.js +6 -6
  58. package/dist/events/json-salvage.d.ts +2 -1
  59. package/dist/events/json-salvage.js +6 -6
  60. package/dist/events/observed-in-window.d.ts +12 -0
  61. package/dist/events/observed-in-window.js +21 -0
  62. package/dist/events/predicate-eval.d.ts +43 -20
  63. package/dist/events/predicate-eval.js +159 -55
  64. package/dist/events/predicate.d.ts +2 -2
  65. package/dist/events/predicate.js +112 -35
  66. package/dist/events/reconcile.js +5 -5
  67. package/dist/events/ring-buffer.js +1 -1
  68. package/dist/events/stale-response.js +6 -6
  69. package/dist/events/testid-near-miss.d.ts +22 -0
  70. package/dist/events/testid-near-miss.js +33 -0
  71. package/dist/events/unit-mismatch.js +8 -8
  72. package/dist/flows/affected-tools.js +19 -4
  73. package/dist/flows/affected.js +1 -1
  74. package/dist/flows/annotate-target.d.ts +1 -0
  75. package/dist/flows/annotate-target.js +20 -0
  76. package/dist/flows/annotate-tools.js +25 -3
  77. package/dist/flows/annotate.js +17 -4
  78. package/dist/flows/assertion-integrity.d.ts +2 -1
  79. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  80. package/dist/flows/assertion-tiers-store.js +2 -2
  81. package/dist/flows/attributed-failure.d.ts +17 -0
  82. package/dist/flows/attributed-failure.js +20 -0
  83. package/dist/flows/buddy-status.d.ts +2 -1
  84. package/dist/flows/buddy-status.js +2 -2
  85. package/dist/flows/coverage.d.ts +3 -2
  86. package/dist/flows/coverage.js +2 -2
  87. package/dist/flows/decision.d.ts +0 -6
  88. package/dist/flows/decision.js +64 -8
  89. package/dist/flows/empty-flow.d.ts +6 -0
  90. package/dist/flows/empty-flow.js +24 -0
  91. package/dist/flows/flake-store.d.ts +0 -1
  92. package/dist/flows/flake-store.js +1 -1
  93. package/dist/flows/flake.d.ts +2 -1
  94. package/dist/flows/flake.js +2 -2
  95. package/dist/flows/flow-classify.js +12 -0
  96. package/dist/flows/flow-replay-run.js +4 -4
  97. package/dist/flows/flow-replay.d.ts +5 -7
  98. package/dist/flows/flow-replay.js +114 -47
  99. package/dist/flows/flow-report.js +1 -1
  100. package/dist/flows/flow-scope.d.ts +1 -6
  101. package/dist/flows/flow-step-runners.d.ts +26 -6
  102. package/dist/flows/flow-step-runners.js +157 -6
  103. package/dist/flows/flow-success.js +20 -15
  104. package/dist/flows/flow-tools.js +67 -13
  105. package/dist/flows/flows.js +15 -11
  106. package/dist/flows/gate.d.ts +4 -3
  107. package/dist/flows/gate.js +1 -1
  108. package/dist/flows/heal-run.d.ts +0 -8
  109. package/dist/flows/heal-run.js +5 -5
  110. package/dist/flows/heal.js +3 -3
  111. package/dist/flows/predicate-to-expect.d.ts +38 -0
  112. package/dist/flows/predicate-to-expect.js +95 -0
  113. package/dist/flows/replay.d.ts +16 -1
  114. package/dist/flows/replay.js +58 -22
  115. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  116. package/dist/flows/role-anchor-nearest.js +45 -0
  117. package/dist/flows/server-verify.d.ts +1 -1
  118. package/dist/flows/server-verify.js +5 -5
  119. package/dist/flows/verify-change-tools.js +32 -10
  120. package/dist/flows/verify-change-verdict.d.ts +15 -0
  121. package/dist/flows/verify-change-verdict.js +25 -0
  122. package/dist/flows/watch-batcher.d.ts +3 -2
  123. package/dist/honesty/already-true.d.ts +2 -0
  124. package/dist/honesty/already-true.js +31 -0
  125. package/dist/honesty/blind-spots.d.ts +16 -3
  126. package/dist/honesty/blind-spots.js +24 -12
  127. package/dist/honesty/honesty.d.ts +3 -2
  128. package/dist/honesty/honesty.js +4 -4
  129. package/dist/honesty/uncaptured-bodies.js +1 -1
  130. package/dist/honesty/unread-outcome.js +2 -2
  131. package/dist/honesty/verified.d.ts +22 -3
  132. package/dist/honesty/verified.js +48 -8
  133. package/dist/http-server.js +14 -7
  134. package/dist/hunt/hunt-report.d.ts +2 -1
  135. package/dist/hunt/hunt-report.js +2 -2
  136. package/dist/index.d.ts +8 -6
  137. package/dist/index.js +32 -25
  138. package/dist/init/agent-rules.d.ts +2 -1
  139. package/dist/init/agent-rules.js +45 -7
  140. package/dist/init/app-choice.d.ts +20 -0
  141. package/dist/init/app-choice.js +25 -0
  142. package/dist/init/astro-host.d.ts +21 -0
  143. package/dist/init/astro-host.js +27 -0
  144. package/dist/init/astro-patch.d.ts +16 -0
  145. package/dist/init/astro-patch.js +240 -0
  146. package/dist/init/capabilities.d.ts +17 -0
  147. package/dist/init/capabilities.js +54 -0
  148. package/dist/init/cra.d.ts +37 -0
  149. package/dist/init/cra.js +90 -0
  150. package/dist/init/cursor.js +39 -4
  151. package/dist/init/desktop-doctor.d.ts +3 -2
  152. package/dist/init/desktop-doctor.js +4 -4
  153. package/dist/init/detect.d.ts +26 -0
  154. package/dist/init/detect.js +78 -5
  155. package/dist/init/mcp-registered.d.ts +12 -0
  156. package/dist/init/mcp-registered.js +14 -0
  157. package/dist/init/mcp.d.ts +27 -2
  158. package/dist/init/mcp.js +40 -4
  159. package/dist/init/next-patch.d.ts +39 -0
  160. package/dist/init/next-patch.js +196 -0
  161. package/dist/init/node-io.js +48 -6
  162. package/dist/init/patch-kind.d.ts +20 -0
  163. package/dist/init/patch-kind.js +10 -0
  164. package/dist/init/plan-framework.d.ts +50 -0
  165. package/dist/init/plan-framework.js +341 -0
  166. package/dist/init/plan.d.ts +119 -6
  167. package/dist/init/plan.js +230 -131
  168. package/dist/init/project-id.d.ts +5 -9
  169. package/dist/init/project-id.js +10 -22
  170. package/dist/init/run.d.ts +22 -0
  171. package/dist/init/run.js +393 -28
  172. package/dist/init/slash-command.d.ts +27 -0
  173. package/dist/init/slash-command.js +61 -0
  174. package/dist/init/snippets.d.ts +41 -3
  175. package/dist/init/snippets.js +185 -10
  176. package/dist/init/vite-config.d.ts +4 -10
  177. package/dist/init/vite-config.js +15 -7
  178. package/dist/init/workspace-apps.d.ts +23 -0
  179. package/dist/init/workspace-apps.js +68 -0
  180. package/dist/input/driven-redaction.d.ts +15 -0
  181. package/dist/input/driven-redaction.js +76 -0
  182. package/dist/input/network-detail.d.ts +9 -3
  183. package/dist/input/network-detail.js +43 -23
  184. package/dist/input/network-mock-tools.js +2 -2
  185. package/dist/input/network-mock.js +4 -4
  186. package/dist/input/real-input.js +8 -3
  187. package/dist/input/scroll-find.js +2 -2
  188. package/dist/input/scroll-tools.d.ts +0 -5
  189. package/dist/input/scroll-tools.js +11 -4
  190. package/dist/input/viewport-tools.js +2 -2
  191. package/dist/journal/ambient-store.d.ts +0 -2
  192. package/dist/journal/ambient-store.js +1 -1
  193. package/dist/journal/ambient.js +1 -1
  194. package/dist/journal/attach-journal.d.ts +2 -1
  195. package/dist/journal/deviation-report.js +6 -6
  196. package/dist/journal/deviation-service.js +1 -1
  197. package/dist/journal/envelope-store.d.ts +0 -2
  198. package/dist/journal/envelope-store.js +1 -1
  199. package/dist/journal/envelope.d.ts +2 -3
  200. package/dist/journal/envelope.js +3 -3
  201. package/dist/journal/journal-query.js +1 -1
  202. package/dist/journal/journal-recorder.d.ts +2 -1
  203. package/dist/journal/journal-recorder.js +2 -2
  204. package/dist/journal/rollups.js +9 -9
  205. package/dist/journal/session-end.d.ts +2 -1
  206. package/dist/journal/session-journal.js +4 -4
  207. package/dist/license/license.d.ts +0 -14
  208. package/dist/license/license.js +8 -8
  209. package/dist/log.d.ts +10 -0
  210. package/dist/log.js +11 -1
  211. package/dist/mcp/mcp-is-error.d.ts +13 -0
  212. package/dist/mcp/mcp-is-error.js +18 -0
  213. package/dist/mcp/mcp-outage.d.ts +31 -0
  214. package/dist/mcp/mcp-outage.js +48 -0
  215. package/dist/mcp/mcp-proxy.d.ts +124 -0
  216. package/dist/mcp/mcp-proxy.js +697 -0
  217. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  218. package/dist/{mcp.js → mcp/mcp.js} +110 -35
  219. package/dist/mcp/proxy-handshake.d.ts +3 -0
  220. package/dist/mcp/proxy-handshake.js +54 -0
  221. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  222. package/dist/mcp/proxy-lifecycle.js +70 -0
  223. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  224. package/dist/mcp/tool-catalog-cache.js +55 -0
  225. package/dist/oracles/propose-consequences.d.ts +2 -1
  226. package/dist/oracles/propose-consequences.js +16 -11
  227. package/dist/oracles/self-instrument.d.ts +2 -1
  228. package/dist/oracles/self-instrument.js +1 -1
  229. package/dist/phenomena/phenomena.js +3 -3
  230. package/dist/platform.d.ts +16 -0
  231. package/dist/platform.js +16 -0
  232. package/dist/pool/playwright-launcher.d.ts +15 -2
  233. package/dist/pool/playwright-launcher.js +21 -2
  234. package/dist/project/fs-port.js +1 -1
  235. package/dist/project/project-tools.js +2 -2
  236. package/dist/runs/build-verification-run.js +1 -1
  237. package/dist/runs/repair-prompt.d.ts +0 -6
  238. package/dist/runs/repair-prompt.js +3 -3
  239. package/dist/runs/risk-classify.js +1 -1
  240. package/dist/runs/run-diff.d.ts +0 -2
  241. package/dist/runs/run-diff.js +3 -3
  242. package/dist/runs/run-tools.js +2 -2
  243. package/dist/runs/verification-sync.js +2 -2
  244. package/dist/runs/verify-http.js +1 -1
  245. package/dist/runs/verify-server.js +1 -1
  246. package/dist/session/command-timeout.d.ts +11 -3
  247. package/dist/session/command-timeout.js +2 -2
  248. package/dist/session/control-envelope.d.ts +6 -0
  249. package/dist/session/control-envelope.js +7 -1
  250. package/dist/session/dev-server-probe.d.ts +13 -0
  251. package/dist/session/dev-server-probe.js +52 -0
  252. package/dist/session/last-act.d.ts +17 -3
  253. package/dist/session/last-act.js +16 -4
  254. package/dist/session/live-control-tools.js +7 -2
  255. package/dist/session/live-control.js +1 -1
  256. package/dist/session/looks-like-dev-server.d.ts +14 -0
  257. package/dist/session/looks-like-dev-server.js +25 -0
  258. package/dist/session/no-session-diagnosis.d.ts +38 -0
  259. package/dist/session/no-session-diagnosis.js +70 -0
  260. package/dist/session/no-session-watch.d.ts +19 -0
  261. package/dist/session/no-session-watch.js +61 -0
  262. package/dist/session/observed-state.d.ts +4 -0
  263. package/dist/session/observed-state.js +19 -1
  264. package/dist/session/output-budget.js +1 -1
  265. package/dist/session/session-health.d.ts +8 -1
  266. package/dist/session/session-health.js +8 -7
  267. package/dist/session/session-info.d.ts +26 -7
  268. package/dist/session/session-info.js +42 -1
  269. package/dist/session/session-lease.d.ts +0 -11
  270. package/dist/session/session-lease.js +1 -1
  271. package/dist/session/session-manager.d.ts +3 -0
  272. package/dist/session/session-manager.js +58 -9
  273. package/dist/session/session-readiness.d.ts +1 -1
  274. package/dist/session/session-readiness.js +6 -1
  275. package/dist/session/session-reaper.js +1 -1
  276. package/dist/session/session-reconnect.d.ts +32 -0
  277. package/dist/session/session-reconnect.js +42 -0
  278. package/dist/session/session.d.ts +20 -2
  279. package/dist/session/session.js +76 -26
  280. package/dist/session/state-select.d.ts +1 -1
  281. package/dist/session/state-select.js +1 -1
  282. package/dist/status-payload.d.ts +19 -0
  283. package/dist/status-payload.js +11 -0
  284. package/dist/telemetry/argument-shape.js +10 -1
  285. package/dist/telemetry/browser-mode.d.ts +27 -0
  286. package/dist/telemetry/browser-mode.js +33 -0
  287. package/dist/telemetry/bug-found.d.ts +17 -21
  288. package/dist/telemetry/bug-found.js +120 -8
  289. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  290. package/dist/telemetry/cli-telemetry.js +27 -4
  291. package/dist/telemetry/daemon-telemetry.d.ts +19 -0
  292. package/dist/telemetry/daemon-telemetry.js +12 -2
  293. package/dist/telemetry/dev-repo.js +4 -4
  294. package/dist/telemetry/error-fingerprint.js +21 -2
  295. package/dist/telemetry/feedback-cli.d.ts +1 -1
  296. package/dist/telemetry/feedback-cli.js +4 -4
  297. package/dist/telemetry/feedback-context.d.ts +22 -0
  298. package/dist/telemetry/feedback-context.js +41 -1
  299. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  300. package/dist/telemetry/feedback-delivery.js +30 -0
  301. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  302. package/dist/telemetry/feedback-outbox.js +87 -0
  303. package/dist/telemetry/feedback.d.ts +22 -1
  304. package/dist/telemetry/feedback.js +67 -8
  305. package/dist/telemetry/git-facts.js +1 -1
  306. package/dist/telemetry/session-metrics.d.ts +2 -0
  307. package/dist/telemetry/session-metrics.js +68 -6
  308. package/dist/telemetry/telemetry.d.ts +27 -2
  309. package/dist/telemetry/telemetry.js +119 -29
  310. package/dist/telemetry/verification-of.d.ts +22 -0
  311. package/dist/telemetry/verification-of.js +84 -0
  312. package/dist/tools/act-capsule.js +2 -2
  313. package/dist/tools/act-danger.d.ts +12 -0
  314. package/dist/tools/act-danger.js +20 -3
  315. package/dist/tools/act-helpers.js +4 -4
  316. package/dist/tools/act-tools.d.ts +0 -8
  317. package/dist/tools/act-tools.js +84 -115
  318. package/dist/tools/act-view.js +7 -7
  319. package/dist/tools/alias-args.d.ts +17 -0
  320. package/dist/tools/alias-args.js +25 -0
  321. package/dist/tools/assert-grade.js +9 -9
  322. package/dist/tools/assert-verdict.d.ts +3 -1
  323. package/dist/tools/assert-verdict.js +8 -5
  324. package/dist/tools/browser-tools.js +30 -11
  325. package/dist/tools/contract-tools.js +1 -1
  326. package/dist/tools/coverage-identity.d.ts +34 -0
  327. package/dist/tools/coverage-identity.js +46 -0
  328. package/dist/tools/coverage-tools.js +10 -14
  329. package/dist/tools/dynamic-tools.d.ts +2 -1
  330. package/dist/tools/dynamic-tools.js +90 -18
  331. package/dist/tools/error-recovery.d.ts +20 -2
  332. package/dist/tools/error-recovery.js +179 -4
  333. package/dist/tools/feedback-tools.d.ts +12 -1
  334. package/dist/tools/feedback-tools.js +28 -5
  335. package/dist/tools/invoke-tool.d.ts +0 -6
  336. package/dist/tools/invoke-tool.js +57 -32
  337. package/dist/tools/lease-hint.d.ts +17 -0
  338. package/dist/tools/lease-hint.js +24 -0
  339. package/dist/tools/lease-tools.js +6 -5
  340. package/dist/tools/merge-tools.d.ts +10 -1
  341. package/dist/tools/merge-tools.js +9 -3
  342. package/dist/tools/merged-name-redirect.d.ts +13 -0
  343. package/dist/tools/merged-name-redirect.js +55 -0
  344. package/dist/tools/navigate-result.d.ts +17 -0
  345. package/dist/tools/navigate-result.js +31 -0
  346. package/dist/tools/observe-tools.js +54 -20
  347. package/dist/tools/observed-nothing.d.ts +25 -0
  348. package/dist/tools/observed-nothing.js +33 -0
  349. package/dist/tools/query-paginate.js +2 -2
  350. package/dist/tools/query-shape.d.ts +18 -0
  351. package/dist/tools/query-shape.js +39 -0
  352. package/dist/tools/read-tools.js +85 -20
  353. package/dist/tools/real-input-attempt.d.ts +28 -0
  354. package/dist/tools/real-input-attempt.js +88 -0
  355. package/dist/tools/reconcile-tools.js +7 -7
  356. package/dist/tools/reload-result.d.ts +14 -0
  357. package/dist/tools/reload-result.js +29 -0
  358. package/dist/tools/snapshot-delta.d.ts +4 -5
  359. package/dist/tools/snapshot-delta.js +0 -0
  360. package/dist/tools/tool-kit.d.ts +8 -0
  361. package/dist/tools/tool-surface.d.ts +83 -0
  362. package/dist/tools/tool-surface.js +204 -0
  363. package/dist/tools/tools-helpers.js +6 -6
  364. package/dist/tools/tools.d.ts +15 -0
  365. package/dist/tools/tools.js +53 -17
  366. package/dist/tools/unadvertised-help.d.ts +5 -0
  367. package/dist/tools/unadvertised-help.js +39 -0
  368. package/dist/trace.d.ts +69 -0
  369. package/dist/trace.js +150 -0
  370. package/dist/update/nudge-credit.d.ts +4 -0
  371. package/dist/update/nudge-credit.js +53 -0
  372. package/dist/update/sdk-sync.d.ts +21 -0
  373. package/dist/update/sdk-sync.js +42 -0
  374. package/dist/update/update-checker.js +3 -3
  375. package/dist/update/update-nudge.d.ts +63 -6
  376. package/dist/update/update-nudge.js +128 -15
  377. package/dist/update/updater.d.ts +1 -9
  378. package/dist/update/updater.js +13 -8
  379. package/dist/version/peer-announce.d.ts +4 -0
  380. package/dist/version/peer-announce.js +30 -0
  381. package/dist/{server-version.js → version/server-version.js} +5 -1
  382. package/dist/version/version-nudge.d.ts +28 -0
  383. package/dist/version/version-nudge.js +40 -0
  384. package/dist/version/version-skew.d.ts +66 -0
  385. package/dist/version/version-skew.js +71 -0
  386. package/dist/visual/visual-diff.js +2 -2
  387. package/dist/visual/visual-tools.js +4 -4
  388. package/docs/agent-cheatsheet.md +85 -0
  389. package/docs/architecture.md +113 -0
  390. package/docs/benchmarks.md +255 -0
  391. package/docs/debugging.md +102 -0
  392. package/docs/deploy-checks.md +69 -0
  393. package/docs/desktop-apps.md +256 -0
  394. package/docs/enterprise.md +62 -0
  395. package/docs/fixtures.md +28 -0
  396. package/docs/flows.md +166 -0
  397. package/docs/getting-started.md +469 -0
  398. package/docs/human-control.md +63 -0
  399. package/docs/integration-patterns.md +171 -0
  400. package/docs/local-registry.md +126 -0
  401. package/docs/mint.json +61 -0
  402. package/docs/multi-agent-testing.md +55 -0
  403. package/docs/platform-integration.md +122 -0
  404. package/docs/telemetry-contract.md +214 -0
  405. package/docs/telemetry.md +184 -0
  406. package/docs/testing.md +82 -0
  407. package/docs/token-efficiency.md +80 -0
  408. package/docs/usage.md +1046 -0
  409. package/package.json +8 -5
  410. package/dist/cli-launch.js +0 -112
  411. package/dist/cli-parse.d.ts +0 -140
  412. package/dist/daemon-resilience.d.ts +0 -22
  413. package/dist/daemon-resilience.js +0 -85
  414. package/dist/mcp-proxy.d.ts +0 -35
  415. package/dist/mcp-proxy.js +0 -190
  416. package/dist/tools/profiles.d.ts +0 -37
  417. package/dist/tools/profiles.js +0 -139
  418. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  419. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  420. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  421. /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
  422. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  423. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  424. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -16,9 +16,35 @@
16
16
  * human never agreed to interrupt. So the envelope carries the fact and the exact command, and the
17
17
  * decision stays with the people whose machine it is.
18
18
  */
19
- import { checkForUpdate } from './update-checker.js';
20
- import { SERVER_VERSION } from '../server-version.js';
19
+ import { checkForUpdate, loadManifest } from './update-checker.js';
20
+ import { SERVER_VERSION } from '../version/server-version.js';
21
21
  import { log } from '../log.js';
22
+ import { creditNudge } from './nudge-credit.js';
23
+ /**
24
+ * Is `candidate` a strictly NEWER release than `current`?
25
+ *
26
+ * Replaces a plain `!==`, which told anyone on a version newer than the published one — a
27
+ * prerelease, a local build, a rollback in progress — to "update" to something older. A nudge that
28
+ * prompts a downgrade is one people learn to ignore, and this is the mechanism the entire adoption
29
+ * story rests on. Numeric per segment, so 2.10.0 correctly beats 2.9.0 (string order does not), and
30
+ * a bare release beats its own prerelease.
31
+ */
32
+ export function isNewerVersion(candidate, current) {
33
+ const parse = (v) => {
34
+ const [core = '', ...rest] = v.split(/[-+]/);
35
+ return { parts: core.split('.').map((n) => Number.parseInt(n, 10) || 0), pre: rest.length > 0 };
36
+ };
37
+ const a = parse(candidate);
38
+ const b = parse(current);
39
+ for (let i = 0; i < 3; i++) {
40
+ const left = a.parts[i] ?? 0;
41
+ const right = b.parts[i] ?? 0;
42
+ if (left !== right)
43
+ return left > right;
44
+ }
45
+ // Same numbers: a final release is newer than its own prerelease, and nothing else counts.
46
+ return !a.pre && b.pre;
47
+ }
22
48
  /** Let the daemon finish coming up before touching the network. */
23
49
  const CHECK_DELAY_MS = 8_000;
24
50
  const UPDATE_COMMAND = 'reticle update';
@@ -29,20 +55,63 @@ let delivered = false;
29
55
  * for 24h on disk, and falls back to the cached manifest when the registry is unreachable — so this
30
56
  * costs one npm registry request per day per machine, and nothing at all when offline.
31
57
  */
58
+ /** How much of the release description may ride on a tool result. See buildNudge. */
59
+ const MAX_CHANGELOG_CHARS = 240;
60
+ const MAX_LISTED_BREAKING = 4;
61
+ /**
62
+ * Describe the release, not just its number.
63
+ *
64
+ * `packages/server/package.json` carries `reticle.changelog` and `reticle.breakingChanges`, and
65
+ * `update-checker` parses both into the manifest — where they stopped. This function named two
66
+ * versions and nothing else, so both fields were written, shipped, parsed and discarded. Latent
67
+ * metadata nobody ever printed.
68
+ *
69
+ * Survivable while releases are additive; actively harmful for one that is not. An agent told only
70
+ * "2.4.1 → 2.5.0" runs `reticle update` mid-task and discovers a retired environment variable and
71
+ * six newly-strict parameters by breaking on them.
72
+ *
73
+ * Bounded on purpose: this rides on a tool result every turn until it is delivered, so a long
74
+ * changelog is a per-turn tax. The count of anything elided is stated rather than silently dropped.
75
+ */
76
+ export function buildNudge(latestVersion, currentVersion, release) {
77
+ const changelog = (release?.changelog ?? '').trim();
78
+ const breaking = (release?.breakingChanges ?? []).filter((line) => 0 < line.trim().length);
79
+ const listed = breaking.slice(0, MAX_LISTED_BREAKING);
80
+ const rest = breaking.length - listed.length;
81
+ return {
82
+ currentVersion,
83
+ latestVersion,
84
+ command: UPDATE_COMMAND,
85
+ action: `A newer Reticle is available (${currentVersion} → ${latestVersion}). ` +
86
+ ('' === changelog ? '' : `${changelog.slice(0, MAX_CHANGELOG_CHARS)} `) +
87
+ (0 === breaking.length
88
+ ? ''
89
+ : `This release has BREAKING changes: ${listed.join('; ')}` +
90
+ (0 < rest ? ` (+${String(rest)} more — see the changelog)` : '') +
91
+ '. Read them before updating. ') +
92
+ `Tell the human, and run \`${UPDATE_COMMAND}\` if they agree — it restarts the daemon, ` +
93
+ 'so do it between tasks rather than mid-verification. Continue your current task first.',
94
+ };
95
+ }
32
96
  export function startUpdateCheck(now = () => Date.now()) {
97
+ // The cached answer first, so the very first tool call can carry it — see armUpdateNudgeFrom.
98
+ armUpdateNudgeFrom(loadManifest());
33
99
  setTimeout(() => {
34
100
  void checkForUpdate(SERVER_VERSION, now)
35
101
  .then((manifest) => {
36
102
  if (!manifest.updateAvailable || manifest.latestVersion === undefined)
37
103
  return;
38
- pending = {
39
- currentVersion: SERVER_VERSION,
40
- latestVersion: manifest.latestVersion,
41
- command: UPDATE_COMMAND,
42
- action: `A newer Reticle is available (${SERVER_VERSION} → ${manifest.latestVersion}). ` +
43
- `Tell the human, and run \`${UPDATE_COMMAND}\` if they agree — it restarts the daemon, ` +
44
- 'so do it between tasks rather than mid-verification. Continue your current task first.',
45
- };
104
+ if (!isNewerVersion(manifest.latestVersion, SERVER_VERSION))
105
+ return;
106
+ // WITH the release description. The manifest has carried `changelog` and `breakingChanges`
107
+ // all along; nothing read them.
108
+ pending = buildNudge(manifest.latestVersion, SERVER_VERSION, {
109
+ ...(manifest.changelog === undefined ? {} : { changelog: manifest.changelog }),
110
+ ...(manifest.breakingChanges === undefined
111
+ ? {}
112
+ : { breakingChanges: manifest.breakingChanges }),
113
+ });
114
+ delivered = false;
46
115
  log('reticle_update_available', {
47
116
  from: SERVER_VERSION,
48
117
  to: manifest.latestVersion,
@@ -53,20 +122,64 @@ export function startUpdateCheck(now = () => Date.now()) {
53
122
  });
54
123
  }, CHECK_DELAY_MS).unref();
55
124
  }
125
+ /**
126
+ * What `reticle update` should install, or undefined when it should do nothing.
127
+ *
128
+ * `handleUpdate` gated on `manifest.updateAvailable`, a plain `latest !== current`, so whenever the
129
+ * registry's latest was OLDER than the running build — a prerelease, a local build, a rollback in
130
+ * progress, a stale npx cache — it announced the move and then installed the downgrade. Reported by
131
+ * a real user on 2026-08-06: "both the `update_available` banner and `reticle update` report the
132
+ * current and target versions swapped, so an upgrade is described as a downgrade."
133
+ */
134
+ export function updateTarget(manifest, currentVersion = SERVER_VERSION) {
135
+ const latest = manifest.latestVersion;
136
+ if (latest === undefined || !isNewerVersion(latest, currentVersion))
137
+ return undefined;
138
+ return latest;
139
+ }
140
+ /**
141
+ * Arm from an already-cached answer, synchronously, at daemon boot.
142
+ *
143
+ * The network check fires 8s after boot and the nudge is delivered by riding a tool result — but
144
+ * half the sessions that use Reticle at all make exactly ONE tool call, usually in the first
145
+ * seconds. The check had not finished, so the single chance to tell them was gone, and the
146
+ * population most in need of the update is precisely the one that never heard about it. Yesterday's
147
+ * answer is already on disk; use it now and let the network refresh behind it.
148
+ */
149
+ export function armUpdateNudgeFrom(cached, currentVersion = SERVER_VERSION) {
150
+ const latest = cached?.latestVersion;
151
+ if (latest === undefined || !isNewerVersion(latest, currentVersion))
152
+ return;
153
+ // The CACHED path matters more than the live one: it is what the very first tool call carries, and
154
+ // it is the only nudge an offline machine ever sees. Dropping the release description here would
155
+ // have left the fields unread on the commonest path while looking fixed.
156
+ pending = buildNudge(latest, currentVersion, {
157
+ ...(cached?.changelog === undefined ? {} : { changelog: cached.changelog }),
158
+ ...(cached?.breakingChanges === undefined ? {} : { breakingChanges: cached.breakingChanges }),
159
+ });
160
+ delivered = false;
161
+ }
56
162
  /** The nudge, once. Returns undefined afterwards so a long session is told exactly one time. */
57
163
  export function takeUpdateNudge() {
58
164
  if (delivered || pending === undefined)
59
165
  return undefined;
60
166
  delivered = true;
167
+ // Leave a mark an `reticle update` in another process can read, so version_changed can say whether
168
+ // this nudge is what caused the upgrade. Best-effort by construction — see nudge-credit.
169
+ creditNudge(pending.latestVersion);
61
170
  return pending;
62
171
  }
172
+ /**
173
+ * The newer version, if the cached answer says there is one — WITHOUT consuming the agent's
174
+ * one-shot nudge. `reticle status` is a human reading a terminal; the two channels are separate
175
+ * audiences and must not steal each other's message.
176
+ */
177
+ export function availableUpdate(currentVersion = SERVER_VERSION) {
178
+ const latest = loadManifest()?.latestVersion;
179
+ return latest !== undefined && isNewerVersion(latest, currentVersion) ? latest : undefined;
180
+ }
63
181
  /** Tests only — drop the module state so each case starts clean. */
64
182
  export function resetUpdateNudge() {
65
183
  pending = undefined;
66
184
  delivered = false;
67
185
  }
68
- /** Tests only — seed a pending nudge without going near the network. */
69
- export function setPendingUpdate(nudge) {
70
- pending = nudge;
71
- delivered = false;
72
- }
@@ -1,5 +1,5 @@
1
1
  /** Which way the installed version moved. A rollback means a release hurt someone enough to retreat. */
2
- export declare const VersionChangeDirection: {
2
+ declare const VersionChangeDirection: {
3
3
  readonly UPDATE: "update";
4
4
  readonly ROLLBACK: "rollback";
5
5
  };
@@ -22,14 +22,6 @@ declare const ExecutionKind: {
22
22
  readonly LOCAL: "local";
23
23
  };
24
24
  type ExecutionKind = (typeof ExecutionKind)[keyof typeof ExecutionKind];
25
- /**
26
- * Infer how reticle was launched from process.argv[1].
27
- *
28
- * npm's npx cache stores packages under a path containing `_npx`, which is the
29
- * most reliable cross-platform signal. Local installs always live inside a
30
- * `node_modules` directory. Everything else is treated as a global install.
31
- */
32
- export declare function detectExecutionKind(): ExecutionKind;
33
25
  /**
34
26
  * Classify a launch from the entry script path. Pure so it's testable. Global installs ALSO live under
35
27
  * a `node_modules`, but under the npm global prefix — a `lib/node_modules` (unix: /usr/local, Homebrew,
@@ -1,14 +1,16 @@
1
1
  import { execFile } from 'node:child_process';
2
+ import { NodePlatform } from '../platform.js';
2
3
  import { existsSync } from 'node:fs';
3
4
  import { platform } from 'node:os';
4
5
  import { dirname, join } from 'node:path';
5
6
  import { loadManifest, saveManifest } from './update-checker.js';
6
- import { RETICLE_NPM_PACKAGE, SERVER_VERSION } from '../server-version.js';
7
+ import { RETICLE_NPM_PACKAGE, SERVER_VERSION } from '../version/server-version.js';
7
8
  import { log } from '../log.js';
8
9
  import { TelemetryEventKind } from '@reticlehq/core';
9
10
  import { getTelemetry } from '../telemetry/telemetry.js';
11
+ import { wasNudged } from './nudge-credit.js';
10
12
  /** Which way the installed version moved. A rollback means a release hurt someone enough to retreat. */
11
- export const VersionChangeDirection = {
13
+ const VersionChangeDirection = {
12
14
  UPDATE: 'update',
13
15
  ROLLBACK: 'rollback',
14
16
  };
@@ -21,15 +23,18 @@ export const VersionChangeDirection = {
21
23
  */
22
24
  export async function reportVersionChange(from, to, direction) {
23
25
  try {
26
+ const nudged = wasNudged(to);
24
27
  await getTelemetry().emit(TelemetryEventKind.VERSION_CHANGED, {
25
- versionChange: { from, to, direction },
28
+ // Only when true: an absent flag reads as "we do not know", which is honest for a machine that
29
+ // updated from a shell script or a package bump with no agent involved at all.
30
+ versionChange: { from, to, direction, ...(nudged ? { nudged } : {}) },
26
31
  });
27
32
  }
28
33
  catch {
29
34
  /* best-effort, like every other send */
30
35
  }
31
36
  }
32
- const NPM_BIN = platform() === 'win32' ? 'npm.cmd' : 'npm';
37
+ const NPM_BIN = NodePlatform.WINDOWS === platform() ? 'npm.cmd' : 'npm';
33
38
  const NPM_TIMEOUT_MS = 120_000;
34
39
  /** How this reticle process was launched — determines which npm strategy to use for updates. */
35
40
  const ExecutionKind = {
@@ -47,7 +52,7 @@ const ExecutionKind = {
47
52
  * most reliable cross-platform signal. Local installs always live inside a
48
53
  * `node_modules` directory. Everything else is treated as a global install.
49
54
  */
50
- export function detectExecutionKind() {
55
+ function detectExecutionKind() {
51
56
  return classifyExecutionKind(process.argv[1] ?? '');
52
57
  }
53
58
  /**
@@ -116,7 +121,7 @@ export function installArgs(version, kind, localRoot) {
116
121
  async function installVersion(version, kind) {
117
122
  const localRoot = kind === ExecutionKind.LOCAL ? findLocalProjectRoot() : null;
118
123
  const plan = installArgs(version, kind, localRoot);
119
- if (plan === null) {
124
+ if (null === plan) {
120
125
  // npx re-resolves the package from npm on the next Claude Code restart — no npm
121
126
  // install needed. The restart itself is what triggers the update.
122
127
  log('reticle_update_npx_strategy', {
@@ -124,7 +129,7 @@ async function installVersion(version, kind) {
124
129
  });
125
130
  return;
126
131
  }
127
- if (kind === ExecutionKind.LOCAL && localRoot === null) {
132
+ if (kind === ExecutionKind.LOCAL && null === localRoot) {
128
133
  // Could not find a project root — fell back to a global install as a safe default.
129
134
  log('reticle_update_local_no_root', { fallback: 'global' });
130
135
  }
@@ -166,7 +171,7 @@ export async function applyUpdate(targetVersion) {
166
171
  */
167
172
  export async function rollback() {
168
173
  const manifest = loadManifest();
169
- if (manifest === null || manifest.previousVersion === undefined) {
174
+ if (null === manifest || manifest.previousVersion === undefined) {
170
175
  throw new Error('No previous version available for rollback');
171
176
  }
172
177
  const prev = manifest.previousVersion;
@@ -0,0 +1,4 @@
1
+ export declare const PEER_VERSION_PARAM = "peerVersion";
2
+ export declare const PEER_CONTRACT_PARAM = "peerContract";
3
+ /** Compare an attaching agent process against this daemon, and queue the nudge when they disagree. */
4
+ export declare function noteAgentPeer(version: string | null, contract: string | null): void;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * How another Reticle PROCESS announces itself to the daemon.
3
+ *
4
+ * The SDK announces in HELLO and the CLI reads /status, but the MCP server an agent spawns had no
5
+ * announcement at all — and it is the piece the user actually hits: `npx @reticlehq/server mcp`
6
+ * resolves from a cache, so an agent can be running a months-old MCP package against a current
7
+ * daemon with nothing anywhere saying so.
8
+ *
9
+ * It rides the SSE connect the proxy already makes, as two query params. Query params rather than a
10
+ * new message because the connection happens before any protocol exchange, and because an older
11
+ * proxy that sends neither is handled correctly by describeSkew (absent contract + differing version
12
+ * = a build that predates the field).
13
+ */
14
+ import { CONTRACT_FINGERPRINT } from '@reticlehq/core';
15
+ import { SERVER_VERSION } from './server-version.js';
16
+ import { describeSkew, DAEMON_FIX, SkewPair } from './version-skew.js';
17
+ import { noteVersionSkew } from './version-nudge.js';
18
+ export const PEER_VERSION_PARAM = 'peerVersion';
19
+ export const PEER_CONTRACT_PARAM = 'peerContract';
20
+ /** Compare an attaching agent process against this daemon, and queue the nudge when they disagree. */
21
+ export function noteAgentPeer(version, contract) {
22
+ const skew = describeSkew({
23
+ what: "the agent's MCP server",
24
+ version: version ?? undefined,
25
+ contract: contract ?? undefined,
26
+ fix: DAEMON_FIX,
27
+ }, { version: SERVER_VERSION, contract: CONTRACT_FINGERPRINT });
28
+ if (skew !== undefined)
29
+ noteVersionSkew(SkewPair.DAEMON, skew);
30
+ }
@@ -1,5 +1,9 @@
1
1
  import { createRequire } from 'node:module';
2
- const _pkg = createRequire(import.meta.url)('../package.json');
2
+ // NOTE: a RUNTIME path, not an import — `tsc` does not check it and a file move will not rewrite
3
+ // it. Two levels up because this file sits in `version/`: from `src/version/` and from
4
+ // `dist/version/` alike, `../../package.json` is the package manifest. Moving this file again means
5
+ // changing this line by hand; nothing else will tell you.
6
+ const _pkg = createRequire(import.meta.url)('../../package.json');
3
7
  /** The Reticle server version, read from package.json at startup. */
4
8
  export const SERVER_VERSION = _pkg.version;
5
9
  /**
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Getting skew in front of the AGENT, on whatever tool it happens to be calling.
3
+ *
4
+ * Every check built before this reported into a place nobody looks: SDK skew landed in
5
+ * `reticle_sessions.versionSkew`, which an agent driving a flow never calls, and daemon skew went to
6
+ * a CLI log line no agent reads. So an agent could work an entire session against a mismatched pair,
7
+ * watch tools behave oddly, and never be told the one fact that explains it.
8
+ *
9
+ * This is the delivery channel — the same one the update nudge uses, for the same reason: an
10
+ * out-of-date install is worth mentioning whatever the agent is doing. It rides out on the next tool
11
+ * result, then goes quiet until the skew CHANGES. Told, not nagged: a banner on every call is one an
12
+ * agent learns to skip, and it would cost those tokens on every call for the rest of the session.
13
+ */
14
+ import type { SkewPair } from './version-skew.js';
15
+ interface VersionSkewNudge {
16
+ pair: SkewPair;
17
+ action: string;
18
+ }
19
+ /**
20
+ * Record a skew for delivery. Re-arms only when the message DIFFERS from the one already delivered:
21
+ * a second tab on a third version is news; the same tab reconnecting is not.
22
+ */
23
+ export declare function noteVersionSkew(pair: SkewPair, action: string): void;
24
+ /** The next undelivered skew, once. */
25
+ export declare function takeVersionSkew(): VersionSkewNudge | undefined;
26
+ /** Tests only — drop the module state so each case starts clean. */
27
+ export declare function resetVersionSkew(): void;
28
+ export {};
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Getting skew in front of the AGENT, on whatever tool it happens to be calling.
3
+ *
4
+ * Every check built before this reported into a place nobody looks: SDK skew landed in
5
+ * `reticle_sessions.versionSkew`, which an agent driving a flow never calls, and daemon skew went to
6
+ * a CLI log line no agent reads. So an agent could work an entire session against a mismatched pair,
7
+ * watch tools behave oddly, and never be told the one fact that explains it.
8
+ *
9
+ * This is the delivery channel — the same one the update nudge uses, for the same reason: an
10
+ * out-of-date install is worth mentioning whatever the agent is doing. It rides out on the next tool
11
+ * result, then goes quiet until the skew CHANGES. Told, not nagged: a banner on every call is one an
12
+ * agent learns to skip, and it would cost those tokens on every call for the rest of the session.
13
+ */
14
+ /** Pending per pair — the SDK and the daemon are different upgrades and each is worth saying once. */
15
+ const pending = new Map();
16
+ /** The last message delivered for each pair, so the same skew is never repeated. */
17
+ const delivered = new Map();
18
+ /**
19
+ * Record a skew for delivery. Re-arms only when the message DIFFERS from the one already delivered:
20
+ * a second tab on a third version is news; the same tab reconnecting is not.
21
+ */
22
+ export function noteVersionSkew(pair, action) {
23
+ if (delivered.get(pair) === action)
24
+ return;
25
+ pending.set(pair, action);
26
+ }
27
+ /** The next undelivered skew, once. */
28
+ export function takeVersionSkew() {
29
+ for (const [pair, action] of pending) {
30
+ pending.delete(pair);
31
+ delivered.set(pair, action);
32
+ return { pair, action };
33
+ }
34
+ return undefined;
35
+ }
36
+ /** Tests only — drop the module state so each case starts clean. */
37
+ export function resetVersionSkew() {
38
+ pending.clear();
39
+ delivered.clear();
40
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Does this peer speak our contract? The daemon is the only thing that asks.
3
+ *
4
+ * Reticle is three separately-installed pieces — the SDK in the page, this daemon, and the MCP
5
+ * server the agent spawns — upgraded independently and therefore drifting constantly. Each pair
6
+ * fails the same silent way: `protocolVersion` still matches, the connection succeeds, and only
7
+ * BEHAVIOUR disagrees. A user hit exactly that with a 2.2.1 SDK against a 2.3.0 daemon and saw a
8
+ * bare `-32000` with nothing on either side naming a version.
9
+ *
10
+ * Two decisions shape this file.
11
+ *
12
+ * The daemon is the SINGLE judge. Every piece announces itself at a connect it already makes — the
13
+ * SDK in HELLO, the CLI and the MCP server over /status — and comparing happens here, once. Three
14
+ * independent pairwise checks would be three copies to keep in step and three separate complaints
15
+ * about one upgrade; a table in one place can say what is actually true: which pieces disagree, and
16
+ * which one is behind.
17
+ *
18
+ * The signal is the CONTRACT FINGERPRINT, not the version. Version equality is wrong in both
19
+ * directions: it fires on 2.4.0-vs-2.4.1 where nothing changed — so every patch release makes every
20
+ * un-upgraded app cry wolf, and the warning becomes noise — and it cannot fire at all for two
21
+ * different BUILDS of one version number, which is exactly what a stale daemon and a cached npx
22
+ * install are. The fingerprint is derived from core's wire vocabulary, so it moves when a name on
23
+ * the wire changes and stays put when internals do. Versions still ride along, because they are what
24
+ * a human acts on.
25
+ */
26
+ /** Which pair disagreed. Named so the nudge can report each independently. */
27
+ export declare const SkewPair: {
28
+ /** The SDK in the page vs this daemon. */
29
+ readonly SDK: "sdk";
30
+ /** Another Reticle process — a CLI, or the MCP server an agent spawned — vs this daemon. */
31
+ readonly DAEMON: "daemon";
32
+ };
33
+ export type SkewPair = (typeof SkewPair)[keyof typeof SkewPair];
34
+ /** What a piece says about itself when it connects. */
35
+ export interface PeerIdentity {
36
+ /** Human-readable subject for the message: "the page", "the daemon on this port". */
37
+ what: string;
38
+ /** Package version, when the peer supplied one. Undefined on a hand-wired connect. */
39
+ version: string | undefined;
40
+ /** Contract fingerprint. Undefined means the peer predates the field — itself evidence of age. */
41
+ contract: string | undefined;
42
+ /** What the human runs to bring this piece into line. */
43
+ fix: string;
44
+ }
45
+ /** Everything a build knows about itself. */
46
+ interface SelfIdentity {
47
+ version: string;
48
+ contract: string;
49
+ }
50
+ /**
51
+ * The one sentence to tell the agent, or undefined when the pair agrees.
52
+ *
53
+ * Silent when the fingerprints MATCH, whatever the versions say — that is the point: a patch bump
54
+ * that changed no wire name is not skew, and warning about it is how a real warning gets ignored.
55
+ *
56
+ * A peer with no fingerprint is treated as skewed ONLY when its version also differs from ours: the
57
+ * field is absent exactly on builds that predate it, so "no fingerprint AND a different version" is
58
+ * a build provably older than this daemon. With neither signal there is nothing to go on, and
59
+ * inventing a warning would be as dishonest as staying silent about a real one.
60
+ */
61
+ export declare function describeSkew(peer: PeerIdentity, self: SelfIdentity): string | undefined;
62
+ /** The fix line for a page whose SDK disagrees with this daemon. */
63
+ export declare function sdkFix(daemonVersion: string): string;
64
+ /** The fix line for another Reticle process that disagrees with the daemon it attached to. */
65
+ export declare const DAEMON_FIX: string;
66
+ export {};
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Does this peer speak our contract? The daemon is the only thing that asks.
3
+ *
4
+ * Reticle is three separately-installed pieces — the SDK in the page, this daemon, and the MCP
5
+ * server the agent spawns — upgraded independently and therefore drifting constantly. Each pair
6
+ * fails the same silent way: `protocolVersion` still matches, the connection succeeds, and only
7
+ * BEHAVIOUR disagrees. A user hit exactly that with a 2.2.1 SDK against a 2.3.0 daemon and saw a
8
+ * bare `-32000` with nothing on either side naming a version.
9
+ *
10
+ * Two decisions shape this file.
11
+ *
12
+ * The daemon is the SINGLE judge. Every piece announces itself at a connect it already makes — the
13
+ * SDK in HELLO, the CLI and the MCP server over /status — and comparing happens here, once. Three
14
+ * independent pairwise checks would be three copies to keep in step and three separate complaints
15
+ * about one upgrade; a table in one place can say what is actually true: which pieces disagree, and
16
+ * which one is behind.
17
+ *
18
+ * The signal is the CONTRACT FINGERPRINT, not the version. Version equality is wrong in both
19
+ * directions: it fires on 2.4.0-vs-2.4.1 where nothing changed — so every patch release makes every
20
+ * un-upgraded app cry wolf, and the warning becomes noise — and it cannot fire at all for two
21
+ * different BUILDS of one version number, which is exactly what a stale daemon and a cached npx
22
+ * install are. The fingerprint is derived from core's wire vocabulary, so it moves when a name on
23
+ * the wire changes and stays put when internals do. Versions still ride along, because they are what
24
+ * a human acts on.
25
+ */
26
+ /** Which pair disagreed. Named so the nudge can report each independently. */
27
+ export const SkewPair = {
28
+ /** The SDK in the page vs this daemon. */
29
+ SDK: 'sdk',
30
+ /** Another Reticle process — a CLI, or the MCP server an agent spawned — vs this daemon. */
31
+ DAEMON: 'daemon',
32
+ };
33
+ function versionPhrase(peer, self) {
34
+ return peer.version === undefined
35
+ ? `${peer.what} is on an unknown version; this daemon is ${self.version}`
36
+ : `${peer.what} is ${peer.version}; this daemon is ${self.version}`;
37
+ }
38
+ /**
39
+ * The one sentence to tell the agent, or undefined when the pair agrees.
40
+ *
41
+ * Silent when the fingerprints MATCH, whatever the versions say — that is the point: a patch bump
42
+ * that changed no wire name is not skew, and warning about it is how a real warning gets ignored.
43
+ *
44
+ * A peer with no fingerprint is treated as skewed ONLY when its version also differs from ours: the
45
+ * field is absent exactly on builds that predate it, so "no fingerprint AND a different version" is
46
+ * a build provably older than this daemon. With neither signal there is nothing to go on, and
47
+ * inventing a warning would be as dishonest as staying silent about a real one.
48
+ */
49
+ export function describeSkew(peer, self) {
50
+ if (peer.contract !== undefined && peer.contract === self.contract)
51
+ return undefined;
52
+ if (peer.contract === undefined) {
53
+ if (peer.version === undefined || peer.version === self.version)
54
+ return undefined;
55
+ return (`version skew: ${versionPhrase(peer, self)}, and it is too old to report which wire contract ` +
56
+ `it speaks. They connect anyway and then disagree about behaviour, which usually surfaces as ` +
57
+ `a bare -32000 with nothing naming a version. ${peer.fix}`);
58
+ }
59
+ return (`version skew: ${versionPhrase(peer, self)}, and they speak DIFFERENT wire contracts ` +
60
+ `(${peer.contract} vs ${self.contract}) — so this is a real mismatch, not a harmless patch ` +
61
+ `difference. Tools will behave in ways neither side reports. ${peer.fix}`);
62
+ }
63
+ /** The fix line for a page whose SDK disagrees with this daemon. */
64
+ export function sdkFix(daemonVersion) {
65
+ return (`Tell the human to install the matching SDK (\`npm i -D @reticlehq/react@${daemonVersion}\`) ` +
66
+ 'or run `reticle update`, then restart their dev server so the page reloads with it.');
67
+ }
68
+ /** The fix line for another Reticle process that disagrees with the daemon it attached to. */
69
+ export const DAEMON_FIX = 'A daemon outlives the agents attached to it, so it keeps serving its OWN code until it restarts ' +
70
+ '— anything fixed in the newer package is simply absent. Run `reticle stop` and retry to replace ' +
71
+ 'it (other agents on that daemon will need to reconnect).';
@@ -32,7 +32,7 @@ function changedRegion(out, width, height) {
32
32
  for (let x = 0; x < width; x += 1) {
33
33
  const i = (y * width + x) * RGBA;
34
34
  // pixelmatch marks a changed pixel solid red (default diffColor) at full alpha.
35
- if (out[i] === 255 && out[i + 1] === 0 && out[i + 2] === 0 && out[i + 3] === 255) {
35
+ if (255 === out[i] && 0 === out[i + 1] && 0 === out[i + 2] && 255 === out[i + 3]) {
36
36
  if (x < minX)
37
37
  minX = x;
38
38
  if (y < minY)
@@ -80,7 +80,7 @@ export async function diffPng(baselineBytes, currentBytes, opts = {}) {
80
80
  const threshold = opts.threshold ?? VISUAL_PIXEL_THRESHOLD;
81
81
  const changedPixels = pixelmatch(a.data, b.data, out.data, width, height, { threshold });
82
82
  const totalPixels = width * height;
83
- const ratio = totalPixels === 0 ? 0 : changedPixels / totalPixels;
83
+ const ratio = 0 === totalPixels ? 0 : changedPixels / totalPixels;
84
84
  const maxRatio = opts.maxRatio ?? 0;
85
85
  const region = changedPixels > 0 ? changedRegion(out.data, width, height) : undefined;
86
86
  const result = {
@@ -16,7 +16,7 @@ const rectShape = z.object({
16
16
  });
17
17
  function screenshotProvider(deps) {
18
18
  const p = deps.realInput;
19
- return p !== undefined && typeof p.screenshot === 'function'
19
+ return p !== undefined && 'function' === typeof p.screenshot
20
20
  ? p
21
21
  : undefined;
22
22
  }
@@ -55,7 +55,7 @@ async function buildOpts(deps, sessionId, args) {
55
55
  if (box !== undefined)
56
56
  return { clip: box };
57
57
  }
58
- return args['fullPage'] === true ? { fullPage: true } : {};
58
+ return true === args['fullPage'] ? { fullPage: true } : {};
59
59
  }
60
60
  /**
61
61
  * Ask the desktop shell to photograph its own window.
@@ -110,7 +110,7 @@ function isCapturePath(path) {
110
110
  function isCompletePng(bytes) {
111
111
  if (bytes.byteLength < 12)
112
112
  return false;
113
- return bytes.subarray(bytes.byteLength - 8, bytes.byteLength - 4).toString('ascii') === 'IEND';
113
+ return 'IEND' === bytes.subarray(bytes.byteLength - 8, bytes.byteLength - 4).toString('ascii');
114
114
  }
115
115
  /**
116
116
  * Get pixels for a session, by whichever route exists.
@@ -137,7 +137,7 @@ async function capture(deps, sessionId, args) {
137
137
  if (png !== undefined)
138
138
  return { png };
139
139
  }
140
- const desktop = await desktopCapture(deps, sessionId, args['fullPage'] === true);
140
+ const desktop = await desktopCapture(deps, sessionId, true === args['fullPage']);
141
141
  if (desktop.png !== undefined)
142
142
  return { png: desktop.png };
143
143
  if (desktop.reason !== undefined)