@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
@@ -12,27 +12,7 @@
12
12
  * npm took 2.3.0 in the next project over.
13
13
  */
14
14
  import { installCommandParts } from '../init/detect.js';
15
- /** Everything we publish is scoped here. */
16
- const RETICLE_SCOPE = '@reticlehq/';
17
- /**
18
- * The CLI. Never an app dependency: it carries the Node MCP server and `ws`, and putting it in a
19
- * browser app's tree is what the retired `@reticlehq/core` umbrella got wrong. The other half of
20
- * `reticle update` owns this one.
21
- */
22
- const SERVER_PACKAGE = '@reticlehq/server';
23
- function namesIn(section) {
24
- if ('object' !== typeof section || null === section)
25
- return [];
26
- return Object.keys(section);
27
- }
28
- /** The Reticle packages this project declares, from either dependency section. */
29
- export function reticleDepsOf(pkgJson) {
30
- if ('object' !== typeof pkgJson || null === pkgJson)
31
- return [];
32
- const manifest = pkgJson;
33
- const declared = [...namesIn(manifest['dependencies']), ...namesIn(manifest['devDependencies'])];
34
- return [...new Set(declared)].filter((name) => name.startsWith(RETICLE_SCOPE) && name !== SERVER_PACKAGE);
35
- }
15
+ export { reticleDepsOf } from './reticle-deps.js';
36
16
  /** The install to run, or null when this project has nothing of ours to sync. */
37
17
  export function sdkSyncCommand(pm, packages, version) {
38
18
  if (0 === packages.length)
@@ -1,13 +1,3 @@
1
- /**
2
- * Is `candidate` a strictly NEWER release than `current`?
3
- *
4
- * Replaces a plain `!==`, which told anyone on a version newer than the published one — a
5
- * prerelease, a local build, a rollback in progress — to "update" to something older. A nudge that
6
- * prompts a downgrade is one people learn to ignore, and this is the mechanism the entire adoption
7
- * story rests on. Numeric per segment, so 2.10.0 correctly beats 2.9.0 (string order does not), and
8
- * a bare release beats its own prerelease.
9
- */
10
- export declare function isNewerVersion(candidate: string, current: string): boolean;
11
1
  interface UpdateNudge {
12
2
  /** The version available, so the agent can tell the human what they would be moving to. */
13
3
  latestVersion: string;
@@ -30,6 +20,13 @@ interface UpdateNudge {
30
20
  *
31
21
  * Bounded on purpose: this rides on a tool result every turn until it is delivered, so a long
32
22
  * changelog is a per-turn tax. The count of anything elided is stated rather than silently dropped.
23
+ *
24
+ * It also names what `update` does to the RULE FILES, which is the half nobody would guess. A new
25
+ * release changes what the always-loaded instructions should say — that is the whole reason
26
+ * `refreshAgentRules` exists — and an agent that reads "it restarts the daemon" has no reason to
27
+ * think its CLAUDE.md is now a release behind. Saying so costs one clause on a message that is
28
+ * delivered once per daemon, and it is the difference between a fleet that upgrades its rules and
29
+ * one that upgrades its binary and keeps last release's instructions forever.
33
30
  */
34
31
  export declare function buildNudge(latestVersion: string, currentVersion: string, release?: {
35
32
  changelog?: string;
@@ -72,6 +69,24 @@ export declare function takeUpdateNudge(): UpdateNudge | undefined;
72
69
  * audiences and must not steal each other's message.
73
70
  */
74
71
  export declare function availableUpdate(currentVersion?: string): string | undefined;
72
+ /**
73
+ * What the nudge did this daemon run, for the session summary.
74
+ *
75
+ * The nudge has shipped for several releases and emitted nothing, so "did the agent get told about
76
+ * a release, and did anything happen" was unanswerable — and it is the whole adoption mechanism for
77
+ * a published fix. `versionChange.nudged` is the half that only ever arrives from machines that DID
78
+ * update; the pinned cohort never fires `version_changed` at all.
79
+ *
80
+ * Reads the same module state the delivery path uses rather than adding a counter beside it, so the
81
+ * two cannot disagree. `shown` is the one-shot delivery flag: it means "an agent was told", never
82
+ * how often. `offered` is present whenever a newer release was known, whether or not it was
83
+ * delivered — without it, `shown: false` would mean "nothing was available" and "something was and
84
+ * the nudge did not fire" at the same time, and only one of those is a defect.
85
+ */
86
+ export declare function updateNudgeState(): {
87
+ shown: boolean;
88
+ offered?: string;
89
+ };
75
90
  /** Tests only — drop the module state so each case starts clean. */
76
91
  export declare function resetUpdateNudge(): void;
77
92
  export {};
@@ -29,7 +29,7 @@ import { creditNudge } from './nudge-credit.js';
29
29
  * story rests on. Numeric per segment, so 2.10.0 correctly beats 2.9.0 (string order does not), and
30
30
  * a bare release beats its own prerelease.
31
31
  */
32
- export function isNewerVersion(candidate, current) {
32
+ function isNewerVersion(candidate, current) {
33
33
  const parse = (v) => {
34
34
  const [core = '', ...rest] = v.split(/[-+]/);
35
35
  return { parts: core.split('.').map((n) => Number.parseInt(n, 10) || 0), pre: rest.length > 0 };
@@ -72,6 +72,13 @@ const MAX_LISTED_BREAKING = 4;
72
72
  *
73
73
  * Bounded on purpose: this rides on a tool result every turn until it is delivered, so a long
74
74
  * changelog is a per-turn tax. The count of anything elided is stated rather than silently dropped.
75
+ *
76
+ * It also names what `update` does to the RULE FILES, which is the half nobody would guess. A new
77
+ * release changes what the always-loaded instructions should say — that is the whole reason
78
+ * `refreshAgentRules` exists — and an agent that reads "it restarts the daemon" has no reason to
79
+ * think its CLAUDE.md is now a release behind. Saying so costs one clause on a message that is
80
+ * delivered once per daemon, and it is the difference between a fleet that upgrades its rules and
81
+ * one that upgrades its binary and keeps last release's instructions forever.
75
82
  */
76
83
  export function buildNudge(latestVersion, currentVersion, release) {
77
84
  const changelog = (release?.changelog ?? '').trim();
@@ -89,8 +96,10 @@ export function buildNudge(latestVersion, currentVersion, release) {
89
96
  : `This release has BREAKING changes: ${listed.join('; ')}` +
90
97
  (0 < rest ? ` (+${String(rest)} more — see the changelog)` : '') +
91
98
  '. 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.',
99
+ `Tell the human, and run \`${UPDATE_COMMAND}\` if they agree — it upgrades the packages, ` +
100
+ "refreshes the Reticle rules in this project's CLAUDE.md / AGENTS.md, and restarts the " +
101
+ 'daemon, so do it between tasks rather than mid-verification. Continue your current task ' +
102
+ 'first.',
94
103
  };
95
104
  }
96
105
  export function startUpdateCheck(now = () => Date.now()) {
@@ -178,6 +187,26 @@ export function availableUpdate(currentVersion = SERVER_VERSION) {
178
187
  const latest = loadManifest()?.latestVersion;
179
188
  return latest !== undefined && isNewerVersion(latest, currentVersion) ? latest : undefined;
180
189
  }
190
+ /**
191
+ * What the nudge did this daemon run, for the session summary.
192
+ *
193
+ * The nudge has shipped for several releases and emitted nothing, so "did the agent get told about
194
+ * a release, and did anything happen" was unanswerable — and it is the whole adoption mechanism for
195
+ * a published fix. `versionChange.nudged` is the half that only ever arrives from machines that DID
196
+ * update; the pinned cohort never fires `version_changed` at all.
197
+ *
198
+ * Reads the same module state the delivery path uses rather than adding a counter beside it, so the
199
+ * two cannot disagree. `shown` is the one-shot delivery flag: it means "an agent was told", never
200
+ * how often. `offered` is present whenever a newer release was known, whether or not it was
201
+ * delivered — without it, `shown: false` would mean "nothing was available" and "something was and
202
+ * the nudge did not fire" at the same time, and only one of those is a defect.
203
+ */
204
+ export function updateNudgeState() {
205
+ return {
206
+ shown: delivered,
207
+ ...(pending === undefined ? {} : { offered: pending.latestVersion }),
208
+ };
209
+ }
181
210
  /** Tests only — drop the module state so each case starts clean. */
182
211
  export function resetUpdateNudge() {
183
212
  pending = undefined;
@@ -3,7 +3,7 @@ declare const VersionChangeDirection: {
3
3
  readonly UPDATE: "update";
4
4
  readonly ROLLBACK: "rollback";
5
5
  };
6
- export type VersionChangeDirection = (typeof VersionChangeDirection)[keyof typeof VersionChangeDirection];
6
+ type VersionChangeDirection = (typeof VersionChangeDirection)[keyof typeof VersionChangeDirection];
7
7
  /**
8
8
  * Report a completed version move. Never throws — an install that worked must still be reported done.
9
9
  *
@@ -13,7 +13,7 @@
13
13
  */
14
14
  import { CONTRACT_FINGERPRINT } from '@reticlehq/core';
15
15
  import { SERVER_VERSION } from './server-version.js';
16
- import { describeSkew, DAEMON_FIX, SkewPair } from './version-skew.js';
16
+ import { daemonFix, describeSkew, SkewPair } from './version-skew.js';
17
17
  import { noteVersionSkew } from './version-nudge.js';
18
18
  export const PEER_VERSION_PARAM = 'peerVersion';
19
19
  export const PEER_CONTRACT_PARAM = 'peerContract';
@@ -23,7 +23,9 @@ export function noteAgentPeer(version, contract) {
23
23
  what: "the agent's MCP server",
24
24
  version: version ?? undefined,
25
25
  contract: contract ?? undefined,
26
- fix: DAEMON_FIX,
26
+ // Inverted from cli-launch: here THIS process is the daemon and the peer is the agent's
27
+ // MCP server, so the daemon is the newer half whenever the announcing agent is behind.
28
+ fix: daemonFix(SERVER_VERSION, version ?? undefined),
27
29
  }, { version: SERVER_VERSION, contract: CONTRACT_FINGERPRINT });
28
30
  if (skew !== undefined)
29
31
  noteVersionSkew(SkewPair.DAEMON, skew);
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The project-aware SDK remedy for version skew (#618).
3
+ *
4
+ * `sdkFix` in version-skew.ts is the no-project fallback: it names `@reticlehq/browser` and npm,
5
+ * because those answers are never actively wrong. When a project directory IS in hand, name the
6
+ * packages actually in package.json and the manager the lockfile implies.
7
+ *
8
+ * Does not import `init/`. The library path must never reach the installer
9
+ * (`library-path-boundary.test.ts`); `reticleDepsOf` lives in `update/` for that reason, and the
10
+ * lockfile → manager map is the same evidence `detectPackageManager` reads, kept here so a HELLO
11
+ * does not load the install plan.
12
+ */
13
+ /** What a project contributes to the remedy, when we were able to read one. */
14
+ export interface SdkFixContext {
15
+ /** `@reticlehq/*` packages declared in package.json. Empty / omitted means none yet. */
16
+ packages?: readonly string[];
17
+ packageManager: PackageManagerName;
18
+ }
19
+ /** Lockfile / marker → manager, same evidence `init/detect.ts` uses. */
20
+ export declare const PackageManagerName: {
21
+ readonly PNPM: "pnpm";
22
+ readonly YARN: "yarn";
23
+ readonly BUN: "bun";
24
+ readonly NPM: "npm";
25
+ };
26
+ export type PackageManagerName = (typeof PackageManagerName)[keyof typeof PackageManagerName];
27
+ /**
28
+ * The one sentence telling the human how to bring the page's SDK in line with this daemon.
29
+ *
30
+ * No context → the framework-neutral sensor and npm, never the React kit. Packages from
31
+ * package.json win when present. When the project has none of ours yet, the sensor is named
32
+ * rather than `@reticlehq/react` — that is the Vue/Nuxt failure.
33
+ */
34
+ export declare function resolveSdkFix(daemonVersion: string, ctx?: Partial<SdkFixContext>): string;
35
+ /**
36
+ * Read the context `resolveSdkFix` needs off a parsed manifest and the lockfiles/markers present.
37
+ *
38
+ * Undefined when there is no manifest to interpret — the caller then uses the no-project fallback.
39
+ * Pure: no filesystem.
40
+ */
41
+ export declare function sdkFixContextOf(pkgJson: unknown, lockfiles: ReadonlySet<string>, nodeModulesMarkers?: ReadonlySet<string>): SdkFixContext | undefined;
42
+ /**
43
+ * The remedy for the project in `directory`, or the no-project fallback when it cannot be read.
44
+ *
45
+ * `read` is injected so the decision is testable without a filesystem; the daemon uses the default.
46
+ */
47
+ export declare function sdkFixForDirectory(daemonVersion: string, directory: string, read?: (path: string) => string | undefined): string;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * The project-aware SDK remedy for version skew (#618).
3
+ *
4
+ * `sdkFix` in version-skew.ts is the no-project fallback: it names `@reticlehq/browser` and npm,
5
+ * because those answers are never actively wrong. When a project directory IS in hand, name the
6
+ * packages actually in package.json and the manager the lockfile implies.
7
+ *
8
+ * Does not import `init/`. The library path must never reach the installer
9
+ * (`library-path-boundary.test.ts`); `reticleDepsOf` lives in `update/` for that reason, and the
10
+ * lockfile → manager map is the same evidence `detectPackageManager` reads, kept here so a HELLO
11
+ * does not load the install plan.
12
+ */
13
+ import { join } from 'node:path';
14
+ import { readFileSync } from 'node:fs';
15
+ import { reticleDepsOf } from '../update/reticle-deps.js';
16
+ /** Lockfile / marker → manager, same evidence `init/detect.ts` uses. */
17
+ export const PackageManagerName = {
18
+ PNPM: 'pnpm',
19
+ YARN: 'yarn',
20
+ BUN: 'bun',
21
+ NPM: 'npm',
22
+ };
23
+ /** Same package a Vue/Nuxt install gets — never the React kit. */
24
+ const FRAMEWORK_NEUTRAL_SDK = '@reticlehq/browser';
25
+ const PACKAGE_JSON = 'package.json';
26
+ const NODE_MODULES = 'node_modules';
27
+ const LOCKFILE_NAMES = ['pnpm-lock.yaml', 'yarn.lock', 'bun.lockb', 'bun.lock'];
28
+ /** Markers each manager leaves inside `node_modules` — stronger than an uncommitted lockfile. */
29
+ const NODE_MODULES_MARKERS = [
30
+ ['.modules.yaml', PackageManagerName.PNPM],
31
+ ['.yarn-state.yml', PackageManagerName.YARN],
32
+ ['.package-lock.json', PackageManagerName.NPM],
33
+ ];
34
+ const INSTALL_FLAGS = {
35
+ [PackageManagerName.PNPM]: ['add', '-D'],
36
+ [PackageManagerName.YARN]: ['add', '-D'],
37
+ [PackageManagerName.BUN]: ['add', '-d'],
38
+ [PackageManagerName.NPM]: ['i', '-D'],
39
+ };
40
+ function packagesFor(ctx) {
41
+ if (ctx?.packages !== undefined && 0 !== ctx.packages.length)
42
+ return ctx.packages;
43
+ return [FRAMEWORK_NEUTRAL_SDK];
44
+ }
45
+ function managerOf(lockfiles, nodeModulesMarkers) {
46
+ if (lockfiles.has('pnpm-lock.yaml'))
47
+ return PackageManagerName.PNPM;
48
+ if (lockfiles.has('yarn.lock'))
49
+ return PackageManagerName.YARN;
50
+ if (lockfiles.has('bun.lockb') || lockfiles.has('bun.lock'))
51
+ return PackageManagerName.BUN;
52
+ for (const [name, pm] of NODE_MODULES_MARKERS) {
53
+ if (nodeModulesMarkers.has(name))
54
+ return pm;
55
+ }
56
+ return PackageManagerName.NPM;
57
+ }
58
+ function installLine(pm, pkgs) {
59
+ return `${pm} ${[...INSTALL_FLAGS[pm], ...pkgs].join(' ')}`;
60
+ }
61
+ function restartClause() {
62
+ return ('or run `reticle update`, then restart their dev server so the page reloads with it. The ' +
63
+ 'restart is not optional: a bundler keeps serving the pre-bundled copy it already has, so an ' +
64
+ 'upgrade can look applied — matching versions in `npm ls` — while the page runs the old module.');
65
+ }
66
+ /**
67
+ * The one sentence telling the human how to bring the page's SDK in line with this daemon.
68
+ *
69
+ * No context → the framework-neutral sensor and npm, never the React kit. Packages from
70
+ * package.json win when present. When the project has none of ours yet, the sensor is named
71
+ * rather than `@reticlehq/react` — that is the Vue/Nuxt failure.
72
+ */
73
+ export function resolveSdkFix(daemonVersion, ctx) {
74
+ const pm = ctx?.packageManager ?? PackageManagerName.NPM;
75
+ const pinned = packagesFor(ctx).map((name) => `${name}@${daemonVersion}`);
76
+ return `Tell the human to install the matching SDK (\`${installLine(pm, pinned)}\`) ${restartClause()}`;
77
+ }
78
+ /**
79
+ * Read the context `resolveSdkFix` needs off a parsed manifest and the lockfiles/markers present.
80
+ *
81
+ * Undefined when there is no manifest to interpret — the caller then uses the no-project fallback.
82
+ * Pure: no filesystem.
83
+ */
84
+ export function sdkFixContextOf(pkgJson, lockfiles, nodeModulesMarkers = new Set()) {
85
+ if ('object' !== typeof pkgJson || null === pkgJson)
86
+ return undefined;
87
+ return {
88
+ packages: reticleDepsOf(pkgJson),
89
+ packageManager: managerOf(lockfiles, nodeModulesMarkers),
90
+ };
91
+ }
92
+ /** Reads a file, or undefined if it is not there / not readable. */
93
+ function readTextFile(path) {
94
+ try {
95
+ return readFileSync(path, 'utf8');
96
+ }
97
+ catch {
98
+ return undefined;
99
+ }
100
+ }
101
+ function parseJson(raw) {
102
+ if (raw === undefined)
103
+ return undefined;
104
+ try {
105
+ return JSON.parse(raw);
106
+ }
107
+ catch {
108
+ return undefined;
109
+ }
110
+ }
111
+ function present(directory, names, read, extra = '') {
112
+ const found = new Set();
113
+ for (const name of names) {
114
+ if (read(join(directory, extra, name)) !== undefined)
115
+ found.add(name);
116
+ }
117
+ return found;
118
+ }
119
+ /**
120
+ * The remedy for the project in `directory`, or the no-project fallback when it cannot be read.
121
+ *
122
+ * `read` is injected so the decision is testable without a filesystem; the daemon uses the default.
123
+ */
124
+ export function sdkFixForDirectory(daemonVersion, directory, read = readTextFile) {
125
+ const ctx = sdkFixContextOf(parseJson(read(join(directory, PACKAGE_JSON))), present(directory, LOCKFILE_NAMES, read), present(directory, NODE_MODULES_MARKERS.map(([name]) => name), read, NODE_MODULES));
126
+ return resolveSdkFix(daemonVersion, ctx);
127
+ }
@@ -23,6 +23,20 @@ interface VersionSkewNudge {
23
23
  export declare function noteVersionSkew(pair: SkewPair, action: string): void;
24
24
  /** The next undelivered skew, once. */
25
25
  export declare function takeVersionSkew(): VersionSkewNudge | undefined;
26
+ /**
27
+ * Splice pending skew onto a tool-error payload.
28
+ *
29
+ * Successful calls already carry `version_skew` via `runTool`. Thrown errors never reached that
30
+ * splice — they became a Playwright string plus `FEEDBACK_ASK`, so a caller debugs a CDP timeout
31
+ * instead of the mismatched pair that caused it (#618). When the error was unrecognized, the
32
+ * feedback ask is replaced: the skew IS the next move, and inviting a bug report about it is
33
+ * backwards. A recognized recovery is kept; the envelope still rides along.
34
+ */
35
+ export declare function takeVersionSkewOnto(payload: {
36
+ error: string;
37
+ recovery?: string;
38
+ feedback?: string;
39
+ }): Record<string, unknown>;
26
40
  /** Tests only — drop the module state so each case starts clean. */
27
41
  export declare function resetVersionSkew(): void;
28
42
  export {};
@@ -11,6 +11,7 @@
11
11
  * result, then goes quiet until the skew CHANGES. Told, not nagged: a banner on every call is one an
12
12
  * agent learns to skip, and it would cost those tokens on every call for the rest of the session.
13
13
  */
14
+ import { EnvelopeKey } from '../tools/tool-kit.js';
14
15
  /** Pending per pair — the SDK and the daemon are different upgrades and each is worth saying once. */
15
16
  const pending = new Map();
16
17
  /** The last message delivered for each pair, so the same skew is never repeated. */
@@ -33,6 +34,26 @@ export function takeVersionSkew() {
33
34
  }
34
35
  return undefined;
35
36
  }
37
+ /**
38
+ * Splice pending skew onto a tool-error payload.
39
+ *
40
+ * Successful calls already carry `version_skew` via `runTool`. Thrown errors never reached that
41
+ * splice — they became a Playwright string plus `FEEDBACK_ASK`, so a caller debugs a CDP timeout
42
+ * instead of the mismatched pair that caused it (#618). When the error was unrecognized, the
43
+ * feedback ask is replaced: the skew IS the next move, and inviting a bug report about it is
44
+ * backwards. A recognized recovery is kept; the envelope still rides along.
45
+ */
46
+ export function takeVersionSkewOnto(payload) {
47
+ const skew = takeVersionSkew();
48
+ if (skew === undefined)
49
+ return payload;
50
+ const { feedback, ...rest } = payload;
51
+ return {
52
+ ...rest,
53
+ ...(feedback !== undefined && rest.recovery === undefined ? { recovery: skew.action } : {}),
54
+ [EnvelopeKey.VERSION_SKEW]: skew,
55
+ };
56
+ }
36
57
  /** Tests only — drop the module state so each case starts clean. */
37
58
  export function resetVersionSkew() {
38
59
  pending.clear();
@@ -59,8 +59,43 @@ interface SelfIdentity {
59
59
  * inventing a warning would be as dishonest as staying silent about a real one.
60
60
  */
61
61
  export declare function describeSkew(peer: PeerIdentity, self: SelfIdentity): string | undefined;
62
- /** The fix line for a page whose SDK disagrees with this daemon. */
62
+ /**
63
+ * The fix line for a page whose SDK disagrees with this daemon, with no project to read.
64
+ *
65
+ * Names the framework-neutral sensor and plain npm, because those are the answers that are never
66
+ * actively wrong. When a project directory IS available, `resolveSdkFix` / `sdkFixForDirectory`
67
+ * in sdk-fix.ts read the real packages and package manager — this used to hardcode
68
+ * `@reticlehq/react` and told Vue projects to install a React package (#618).
69
+ */
63
70
  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;
71
+ /**
72
+ * The fix line for another Reticle process that disagrees with the daemon it attached to.
73
+ *
74
+ * Branches on DIRECTION, which the old unconditional `reticle stop` did not (#618). Restarting the
75
+ * daemon converges the pair only when the daemon is the OLDER half. When the daemon is newer — the
76
+ * common case, because the MCP registration is deliberately unpinned and so resolves fresh while a
77
+ * long-lived daemon does not — `reticle stop` starts another newer daemon and leaves the same two
78
+ * contract hashes with the roles unchanged. Nothing the agent can do converges that, so say so and
79
+ * ask for the one thing that does, exactly as the SDK branch already does when the page is stale.
80
+ *
81
+ * Both versions are named by ROLE, not by which side is calling: this is read from both ends — a
82
+ * CLI judging the daemon it attached to, and the daemon judging an agent that announced itself —
83
+ * and the advice depends on which piece is behind, not on who noticed.
84
+ */
85
+ export declare function daemonFix(daemonVersion: string | undefined, agentVersion: string | undefined): string;
86
+ /**
87
+ * Playwright wording when a CDP call cannot bind to the page.
88
+ *
89
+ * Under SDK/daemon version skew this text is a LIE: the page is still connected (DOM tools work),
90
+ * and the agent spends turns re-acquiring leases and asking a human to sign into a fresh profile
91
+ * (#688). Matched only so we can replace it with the skew sentence already on the session.
92
+ */
93
+ export declare const PLAYWRIGHT_CLOSED_ERROR: RegExp;
94
+ /** True when `error` is Playwright's closed-target class — not proof the tab is gone. */
95
+ export declare function isPlaywrightClosedError(error: unknown): boolean;
96
+ /**
97
+ * Replace a closed-target throw with the session's skew sentence when we already know the pair is
98
+ * mismatched. Undefined when there is no skew to name, or the throw is something else.
99
+ */
100
+ export declare function rewriteClosedAsSkew(error: unknown, skew: string | undefined): Error | undefined;
66
101
  export {};
@@ -23,6 +23,7 @@
23
23
  * the wire changes and stays put when internals do. Versions still ride along, because they are what
24
24
  * a human acts on.
25
25
  */
26
+ import { resolveSdkFix } from './sdk-fix.js';
26
27
  /** Which pair disagreed. Named so the nudge can report each independently. */
27
28
  export const SkewPair = {
28
29
  /** The SDK in the page vs this daemon. */
@@ -60,12 +61,87 @@ export function describeSkew(peer, self) {
60
61
  `(${peer.contract} vs ${self.contract}) — so this is a real mismatch, not a harmless patch ` +
61
62
  `difference. Tools will behave in ways neither side reports. ${peer.fix}`);
62
63
  }
63
- /** The fix line for a page whose SDK disagrees with this daemon. */
64
+ /**
65
+ * The fix line for a page whose SDK disagrees with this daemon, with no project to read.
66
+ *
67
+ * Names the framework-neutral sensor and plain npm, because those are the answers that are never
68
+ * actively wrong. When a project directory IS available, `resolveSdkFix` / `sdkFixForDirectory`
69
+ * in sdk-fix.ts read the real packages and package manager — this used to hardcode
70
+ * `@reticlehq/react` and told Vue projects to install a React package (#618).
71
+ */
64
72
  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.');
73
+ return resolveSdkFix(daemonVersion);
74
+ }
75
+ /**
76
+ * Compare two of our own version strings. Numeric dotted compare, prerelease tags ignored: both
77
+ * sides are Reticle package versions, so this never sees a range or build metadata. Undefined when
78
+ * either side does not parse, so the caller can decline to guess a direction.
79
+ */
80
+ function compareVersions(a, b) {
81
+ const parts = (v) => {
82
+ const head = v.split('-')[0];
83
+ if (head === undefined)
84
+ return undefined;
85
+ const nums = head.split('.').map((n) => parseInt(n, 10));
86
+ return nums.some(isNaN) ? undefined : nums;
87
+ };
88
+ if (a === undefined)
89
+ return undefined;
90
+ const left = parts(a);
91
+ const right = parts(b);
92
+ if (left === undefined || right === undefined)
93
+ return undefined;
94
+ for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
95
+ const diff = (left[i] ?? 0) - (right[i] ?? 0);
96
+ if (0 !== diff)
97
+ return diff;
98
+ }
99
+ return 0;
100
+ }
101
+ /**
102
+ * The fix line for another Reticle process that disagrees with the daemon it attached to.
103
+ *
104
+ * Branches on DIRECTION, which the old unconditional `reticle stop` did not (#618). Restarting the
105
+ * daemon converges the pair only when the daemon is the OLDER half. When the daemon is newer — the
106
+ * common case, because the MCP registration is deliberately unpinned and so resolves fresh while a
107
+ * long-lived daemon does not — `reticle stop` starts another newer daemon and leaves the same two
108
+ * contract hashes with the roles unchanged. Nothing the agent can do converges that, so say so and
109
+ * ask for the one thing that does, exactly as the SDK branch already does when the page is stale.
110
+ *
111
+ * Both versions are named by ROLE, not by which side is calling: this is read from both ends — a
112
+ * CLI judging the daemon it attached to, and the daemon judging an agent that announced itself —
113
+ * and the advice depends on which piece is behind, not on who noticed.
114
+ */
115
+ export function daemonFix(daemonVersion, agentVersion) {
116
+ const order = agentVersion === undefined ? undefined : compareVersions(daemonVersion, agentVersion);
117
+ if (order !== undefined && order > 0) {
118
+ return ('The daemon is NEWER than this process, so `reticle stop` will not converge them — it would ' +
119
+ 'replace it with another newer daemon and leave the same mismatch. Tell the human to restart ' +
120
+ 'the agent so it spawns a current MCP server (or update the package it spawns), then retry.');
121
+ }
122
+ return ('A daemon outlives the agents attached to it, so it keeps serving its OWN code until it restarts ' +
123
+ '— anything fixed in the newer package is simply absent. Run `reticle stop` and retry to replace ' +
124
+ 'it (other agents on that daemon will need to reconnect).');
125
+ }
126
+ /**
127
+ * Playwright wording when a CDP call cannot bind to the page.
128
+ *
129
+ * Under SDK/daemon version skew this text is a LIE: the page is still connected (DOM tools work),
130
+ * and the agent spends turns re-acquiring leases and asking a human to sign into a fresh profile
131
+ * (#688). Matched only so we can replace it with the skew sentence already on the session.
132
+ */
133
+ export const PLAYWRIGHT_CLOSED_ERROR = /target (?:page|context|browser) has been closed|browser has been closed|browser has disconnected/i;
134
+ /** True when `error` is Playwright's closed-target class — not proof the tab is gone. */
135
+ export function isPlaywrightClosedError(error) {
136
+ const message = error instanceof Error ? error.message : String(error);
137
+ return PLAYWRIGHT_CLOSED_ERROR.test(message);
138
+ }
139
+ /**
140
+ * Replace a closed-target throw with the session's skew sentence when we already know the pair is
141
+ * mismatched. Undefined when there is no skew to name, or the throw is something else.
142
+ */
143
+ export function rewriteClosedAsSkew(error, skew) {
144
+ if (skew === undefined || !isPlaywrightClosedError(error))
145
+ return undefined;
146
+ return new Error(skew);
67
147
  }
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).';
@@ -8,15 +8,15 @@ export declare class VisualStore {
8
8
  constructor(fs: FileSystemPort, root: string);
9
9
  /** The absolute baseline path for `name` (for echoing back to the agent). Guards the name so a
10
10
  * crafted value can never echo a path outside .reticle/visual/ to a future caller that does IO. */
11
- baselinePath(name: string): string;
11
+ baselinePath(name: string, runtime?: string): string;
12
12
  /** The absolute overlay-diff path for `name`. Same name guard as baselinePath. */
13
- diffPath(name: string): string;
13
+ diffPath(name: string, runtime?: string): string;
14
14
  /** True iff a valid name and a baseline PNG exists on disk. */
15
- hasBaseline(name: string): Promise<boolean>;
15
+ hasBaseline(name: string, runtime?: string): Promise<boolean>;
16
16
  /** Write a PNG baseline (auto-creating .reticle/visual/). Returns its path; throws on a bad name. */
17
- saveBaseline(name: string, png: Uint8Array): Promise<string>;
17
+ saveBaseline(name: string, png: Uint8Array, runtime?: string): Promise<string>;
18
18
  /** Read a PNG baseline, or undefined if absent / invalid name (never throws on missing). */
19
- readBaseline(name: string): Promise<Uint8Array | undefined>;
19
+ readBaseline(name: string, runtime?: string): Promise<Uint8Array | undefined>;
20
20
  /** Write the overlay diff PNG for `name` (auto-creating .reticle/visual/). Returns its path. */
21
- saveDiff(name: string, png: Uint8Array): Promise<string>;
21
+ saveDiff(name: string, png: Uint8Array, runtime?: string): Promise<string>;
22
22
  }
@@ -1,4 +1,4 @@
1
- import { reticleDirPaths, isValidFlowName, visualDiffPath, visualPath, } from '../project/reticle-dir.js';
1
+ import { isValidFlowName, visualDiffPath, visualDir, visualPath } from '../project/reticle-dir.js';
2
2
  /**
3
3
  * On-disk PNG baselines + diffs under .reticle/visual/. Binary sibling of FlowStore — same
4
4
  * name guard (rejects path traversal before any join) and injected FileSystemPort, but bytes not text.
@@ -12,39 +12,40 @@ export class VisualStore {
12
12
  }
13
13
  /** The absolute baseline path for `name` (for echoing back to the agent). Guards the name so a
14
14
  * crafted value can never echo a path outside .reticle/visual/ to a future caller that does IO. */
15
- baselinePath(name) {
15
+ baselinePath(name, runtime) {
16
16
  if (!isValidFlowName(name))
17
17
  throw new Error(`invalid visual baseline name: ${name}`);
18
- return visualPath(this.#root, name);
18
+ return visualPath(this.#root, name, runtime);
19
19
  }
20
20
  /** The absolute overlay-diff path for `name`. Same name guard as baselinePath. */
21
- diffPath(name) {
21
+ diffPath(name, runtime) {
22
22
  if (!isValidFlowName(name))
23
23
  throw new Error(`invalid visual diff name: ${name}`);
24
- return visualDiffPath(this.#root, name);
24
+ return visualDiffPath(this.#root, name, runtime);
25
25
  }
26
26
  /** True iff a valid name and a baseline PNG exists on disk. */
27
- async hasBaseline(name) {
27
+ async hasBaseline(name, runtime) {
28
28
  if (!isValidFlowName(name))
29
29
  return false;
30
- return this.#fs.exists(visualPath(this.#root, name));
30
+ return this.#fs.exists(visualPath(this.#root, name, runtime));
31
31
  }
32
32
  /** Write a PNG baseline (auto-creating .reticle/visual/). Returns its path; throws on a bad name. */
33
- async saveBaseline(name, png) {
33
+ async saveBaseline(name, png, runtime) {
34
34
  if (!isValidFlowName(name))
35
35
  throw new Error(`invalid visual baseline name: ${name}`);
36
- await this.#fs.mkdir(reticleDirPaths(this.#root).visual);
37
- const path = visualPath(this.#root, name);
36
+ // The runtime's own directory, not the shared one — see visualDir.
37
+ await this.#fs.mkdir(visualDir(this.#root, runtime));
38
+ const path = visualPath(this.#root, name, runtime);
38
39
  await this.#fs.writeFileBytes(path, png);
39
40
  return path;
40
41
  }
41
42
  /** Read a PNG baseline, or undefined if absent / invalid name (never throws on missing). */
42
- async readBaseline(name) {
43
- if (!isValidFlowName(name) || !(await this.#fs.exists(visualPath(this.#root, name)))) {
43
+ async readBaseline(name, runtime) {
44
+ if (!isValidFlowName(name) || !(await this.#fs.exists(visualPath(this.#root, name, runtime)))) {
44
45
  return undefined;
45
46
  }
46
47
  try {
47
- return await this.#fs.readFileBytes(visualPath(this.#root, name));
48
+ return await this.#fs.readFileBytes(visualPath(this.#root, name, runtime));
48
49
  }
49
50
  catch (error) {
50
51
  if (this.#fs.isNotFound(error))
@@ -53,11 +54,12 @@ export class VisualStore {
53
54
  }
54
55
  }
55
56
  /** Write the overlay diff PNG for `name` (auto-creating .reticle/visual/). Returns its path. */
56
- async saveDiff(name, png) {
57
+ async saveDiff(name, png, runtime) {
57
58
  if (!isValidFlowName(name))
58
59
  throw new Error(`invalid visual baseline name: ${name}`);
59
- await this.#fs.mkdir(reticleDirPaths(this.#root).visual);
60
- const path = visualDiffPath(this.#root, name);
60
+ // Beside its baseline, or a diff lands in a directory the baseline is not in.
61
+ await this.#fs.mkdir(visualDir(this.#root, runtime));
62
+ const path = visualDiffPath(this.#root, name, runtime);
61
63
  await this.#fs.writeFileBytes(path, png);
62
64
  return path;
63
65
  }