@reticlehq/server 2.13.0 → 2.14.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 (451) hide show
  1. package/SKILL.md +8 -18
  2. package/bin/reticle.js +3 -0
  3. package/dist/bridge/auth-failure-reason.js +12 -1
  4. package/dist/bridge/bridge-security.d.ts +8 -0
  5. package/dist/bridge/bridge-security.js +6 -1
  6. package/dist/bridge/bridge.d.ts +3 -0
  7. package/dist/bridge/bridge.js +23 -1
  8. package/dist/bridge/bridge.test-harness.d.ts +10 -1
  9. package/dist/bridge/bridge.test-harness.js +7 -1
  10. package/dist/bridge/pairing-token.d.ts +17 -0
  11. package/dist/bridge/pairing-token.js +26 -0
  12. package/dist/bridge/token-auth.d.ts +7 -0
  13. package/dist/bridge/token-auth.js +7 -1
  14. package/dist/capsule/capsule-store.d.ts +4 -4
  15. package/dist/capsule/capsule.d.ts +1 -1
  16. package/dist/capsule/capsule.js +3 -3
  17. package/dist/capsule/causal-summary.d.ts +21 -1
  18. package/dist/capsule/causal-summary.js +8 -6
  19. package/dist/capsule/divergence.d.ts +1 -1
  20. package/dist/capsule/divergence.js +26 -10
  21. package/dist/chromium-launch-options.d.ts +2 -1
  22. package/dist/cli/adhoc-verdict.d.ts +58 -0
  23. package/dist/cli/adhoc-verdict.js +115 -0
  24. package/dist/cli/chromium-hint.d.ts +18 -4
  25. package/dist/cli/chromium-hint.js +54 -6
  26. package/dist/cli/cli-doctor.js +1 -2
  27. package/dist/cli/cli-flow-commands.d.ts +16 -2
  28. package/dist/cli/cli-flow-commands.js +28 -9
  29. package/dist/cli/cli-parse-feedback.js +1 -1
  30. package/dist/cli/cli-parse.d.ts +2 -1
  31. package/dist/cli/cli-parse.js +38 -0
  32. package/dist/cli/cli-port.d.ts +5 -13
  33. package/dist/cli/cli-port.js +5 -38
  34. package/dist/cli/cli-update-commands.js +4 -4
  35. package/dist/cli/cli-verify.d.ts +24 -7
  36. package/dist/cli/cli-verify.js +87 -13
  37. package/dist/cli/cloud-keystore.d.ts +2 -1
  38. package/dist/cli/cloud-login.d.ts +2 -1
  39. package/dist/cli/cloud-session.d.ts +2 -1
  40. package/dist/cli/cloud-session.js +1 -1
  41. package/dist/cli/daemon-start-options.d.ts +2 -1
  42. package/dist/cli/device-flow.d.ts +2 -2
  43. package/dist/cli/doctor-daemon-line.d.ts +2 -1
  44. package/dist/cli/doctor-project-line.d.ts +2 -1
  45. package/dist/cli/doctor-rows.d.ts +2 -1
  46. package/dist/cli/drive-attach.d.ts +4 -3
  47. package/dist/cli/gate-hook-message.d.ts +2 -1
  48. package/dist/cli/status-next-action.js +10 -11
  49. package/dist/cli.d.ts +1 -0
  50. package/dist/cli.js +21 -9
  51. package/dist/cloud/account-state.d.ts +20 -0
  52. package/dist/cloud/account-state.js +65 -0
  53. package/dist/cloud/cloud-sync.d.ts +46 -3
  54. package/dist/cloud/cloud-sync.js +116 -0
  55. package/dist/cloud/sync-cycle.d.ts +5 -4
  56. package/dist/cloud/sync-cycle.js +1 -1
  57. package/dist/cloud/sync-daemon.d.ts +3 -19
  58. package/dist/cloud/sync-daemon.js +2 -2
  59. package/dist/crawl/crawl-tools.js +1 -1
  60. package/dist/crawl/crawl.d.ts +5 -5
  61. package/dist/crawl/crawl.js +138 -4
  62. package/dist/daemon/daemon-resilience.d.ts +3 -2
  63. package/dist/daemon/daemon-resilience.js +43 -4
  64. package/dist/daemon/daemon-resolve.d.ts +0 -9
  65. package/dist/daemon/daemon-resolve.js +2 -2
  66. package/dist/daemon/dev-servers.d.ts +1 -1
  67. package/dist/daemon/dev-servers.js +1 -1
  68. package/dist/daemon/loopback-alias.d.ts +2 -1
  69. package/dist/daemon/startup-failure.d.ts +11 -0
  70. package/dist/daemon/startup-failure.js +46 -0
  71. package/dist/events/contradictions.d.ts +10 -16
  72. package/dist/events/contradictions.js +149 -17
  73. package/dist/events/declared.d.ts +13 -1
  74. package/dist/events/declared.js +59 -3
  75. package/dist/events/echo-mismatch.js +75 -6
  76. package/dist/events/lineage.d.ts +52 -0
  77. package/dist/events/lineage.js +122 -0
  78. package/dist/events/net-evidence.d.ts +21 -0
  79. package/dist/events/net-evidence.js +81 -0
  80. package/dist/events/predicate-console.d.ts +13 -0
  81. package/dist/events/predicate-console.js +138 -0
  82. package/dist/events/predicate-element.d.ts +13 -0
  83. package/dist/events/predicate-element.js +214 -0
  84. package/dist/events/predicate-eval.d.ts +15 -3
  85. package/dist/events/predicate-eval.js +128 -110
  86. package/dist/events/predicate-parse.js +34 -1
  87. package/dist/events/predicate-request-body.d.ts +50 -0
  88. package/dist/events/predicate-request-body.js +120 -0
  89. package/dist/events/predicate-route.d.ts +21 -1
  90. package/dist/events/predicate-route.js +149 -21
  91. package/dist/events/predicate-schema.d.ts +8 -1
  92. package/dist/events/predicate-schema.js +34 -0
  93. package/dist/events/predicate.d.ts +2 -0
  94. package/dist/events/predicate.js +52 -159
  95. package/dist/events/split-text-miss.d.ts +6 -4
  96. package/dist/events/split-text-miss.js +12 -5
  97. package/dist/flows/affected-tools.js +22 -9
  98. package/dist/flows/annotate.d.ts +2 -1
  99. package/dist/flows/annotate.js +19 -1
  100. package/dist/flows/coverage.d.ts +2 -2
  101. package/dist/flows/flake.d.ts +3 -3
  102. package/dist/flows/flow-expect-grammar.d.ts +33 -0
  103. package/dist/flows/flow-expect-grammar.js +155 -0
  104. package/dist/flows/flow-replay-run.d.ts +1 -1
  105. package/dist/flows/flow-replay-run.js +28 -15
  106. package/dist/flows/flow-replay.d.ts +10 -0
  107. package/dist/flows/flow-replay.js +57 -22
  108. package/dist/flows/flow-secret-field.d.ts +14 -0
  109. package/dist/flows/flow-secret-field.js +22 -0
  110. package/dist/flows/flow-step-runners.d.ts +1 -0
  111. package/dist/flows/flow-step-runners.js +91 -34
  112. package/dist/flows/flow-success.js +22 -0
  113. package/dist/flows/flow-tools.js +27 -8
  114. package/dist/flows/flow-upload-resolve.d.ts +23 -0
  115. package/dist/flows/flow-upload-resolve.js +46 -0
  116. package/dist/flows/flows.d.ts +15 -0
  117. package/dist/flows/flows.js +17 -14
  118. package/dist/flows/git-changed.d.ts +23 -4
  119. package/dist/flows/git-changed.js +32 -6
  120. package/dist/flows/heal-run.js +2 -2
  121. package/dist/flows/predicate-to-expect.d.ts +3 -2
  122. package/dist/flows/predicate-to-expect.js +11 -5
  123. package/dist/flows/recording-backtrack.d.ts +24 -0
  124. package/dist/flows/recording-backtrack.js +73 -0
  125. package/dist/flows/recordings.d.ts +6 -0
  126. package/dist/flows/replay-halt.d.ts +26 -0
  127. package/dist/flows/replay-halt.js +30 -0
  128. package/dist/flows/replay.d.ts +0 -8
  129. package/dist/flows/replay.js +15 -3
  130. package/dist/flows/suite-flakes.d.ts +2 -1
  131. package/dist/flows/verify-change-tools.js +16 -4
  132. package/dist/honesty/already-true.d.ts +14 -0
  133. package/dist/honesty/already-true.js +47 -4
  134. package/dist/honesty/blind-spots.d.ts +3 -1
  135. package/dist/honesty/blind-spots.js +24 -1
  136. package/dist/honesty/body-capture-remedy.d.ts +42 -0
  137. package/dist/honesty/body-capture-remedy.js +95 -0
  138. package/dist/honesty/feature-capture.d.ts +3 -2
  139. package/dist/honesty/feature-capture.js +1 -1
  140. package/dist/honesty/gap-ledger.d.ts +1 -1
  141. package/dist/honesty/gap-ledger.js +1 -1
  142. package/dist/honesty/instrumentation-gaps.d.ts +8 -1
  143. package/dist/honesty/instrumentation-gaps.js +12 -2
  144. package/dist/honesty/observability.d.ts +4 -3
  145. package/dist/honesty/tool-hit-rate.d.ts +3 -2
  146. package/dist/honesty/uncaptured-bodies.d.ts +3 -1
  147. package/dist/honesty/uncaptured-bodies.js +6 -2
  148. package/dist/honesty/unsettled.d.ts +9 -0
  149. package/dist/honesty/unsettled.js +45 -0
  150. package/dist/honesty/verified.d.ts +32 -1
  151. package/dist/honesty/verified.js +37 -10
  152. package/dist/impact/impact-store.d.ts +5 -14
  153. package/dist/impact/impact-store.js +49 -6
  154. package/dist/index.js +1 -1
  155. package/dist/input/network-detail.d.ts +26 -1
  156. package/dist/input/network-detail.js +44 -12
  157. package/dist/input/network-mock-tools.js +24 -17
  158. package/dist/input/real-input.js +1 -1
  159. package/dist/input/viewport-tools.js +25 -13
  160. package/dist/intent/inline-intent.d.ts +0 -10
  161. package/dist/intent/inline-intent.js +1 -1
  162. package/dist/intent/intent-shard-store.d.ts +3 -2
  163. package/dist/intent/intent-shard-store.js +1 -1
  164. package/dist/intent/intent-shard.d.ts +14 -14
  165. package/dist/intent/intent-shard.js +4 -4
  166. package/dist/intent/intent-store.d.ts +1 -1
  167. package/dist/intent/intent-store.js +1 -1
  168. package/dist/intent/intent-subject.d.ts +2 -1
  169. package/dist/intent/open-intents.d.ts +3 -2
  170. package/dist/license/license.d.ts +4 -4
  171. package/dist/license/license.js +1 -1
  172. package/dist/mcp/attach-memory.d.ts +2 -1
  173. package/dist/mcp/client-identity.d.ts +2 -1
  174. package/dist/mcp/mcp-outage.d.ts +1 -1
  175. package/dist/mcp/mcp-outage.js +1 -1
  176. package/dist/mcp/mcp-proxy.d.ts +3 -1
  177. package/dist/mcp/mcp-proxy.js +32 -7
  178. package/dist/mcp/mcp.js +3 -1
  179. package/dist/mcp/proxy-handshake.d.ts +2 -1
  180. package/dist/mcp/proxy-idle-exit.d.ts +45 -0
  181. package/dist/mcp/proxy-idle-exit.js +94 -0
  182. package/dist/mcp/server-instructions.d.ts +2 -1
  183. package/dist/memory/project-memory.d.ts +2 -3
  184. package/dist/memory/project-memory.js +1 -1
  185. package/dist/oracles/propose-consequences.js +5 -5
  186. package/dist/oracles/self-instrument.d.ts +2 -2
  187. package/dist/oracles/self-instrument.js +1 -1
  188. package/dist/platform.d.ts +3 -5
  189. package/dist/platform.js +3 -5
  190. package/dist/pool/browser-pool.d.ts +92 -0
  191. package/dist/pool/browser-pool.js +119 -5
  192. package/dist/pool/playwright-launcher.js +29 -2
  193. package/dist/pool/storage-seed.d.ts +23 -0
  194. package/dist/pool/storage-seed.js +198 -0
  195. package/dist/project/artifact-root.d.ts +2 -1
  196. package/dist/project/memory-fs.d.ts +2 -1
  197. package/dist/project/reticle-dir.js +1 -1
  198. package/dist/project/session-root.d.ts +8 -0
  199. package/dist/project/session-root.js +9 -2
  200. package/dist/runs/reticle-runner.d.ts +14 -1
  201. package/dist/runs/reticle-runner.js +39 -2
  202. package/dist/session/attachment-history.d.ts +2 -1
  203. package/dist/session/fake-session.d.ts +37 -0
  204. package/dist/session/fake-session.js +81 -0
  205. package/dist/session/lease-visibility.d.ts +2 -1
  206. package/dist/session/live-control-tools.js +6 -0
  207. package/dist/session/no-session-diagnosis.d.ts +17 -1
  208. package/dist/session/no-session-diagnosis.js +32 -6
  209. package/dist/session/no-session-next-action.d.ts +14 -0
  210. package/dist/session/no-session-next-action.js +18 -0
  211. package/dist/session/no-session-watch.js +46 -3
  212. package/dist/session/registered-projects.d.ts +54 -0
  213. package/dist/session/registered-projects.js +73 -0
  214. package/dist/session/review-store.d.ts +2 -1
  215. package/dist/session/session-health.d.ts +62 -1
  216. package/dist/session/session-health.js +82 -2
  217. package/dist/session/session-manager.d.ts +11 -1
  218. package/dist/session/session-manager.js +18 -3
  219. package/dist/session/session-replaced.d.ts +24 -0
  220. package/dist/session/session-replaced.js +29 -0
  221. package/dist/session/session-successor.js +20 -4
  222. package/dist/session/session.d.ts +13 -5
  223. package/dist/session/session.js +31 -23
  224. package/dist/setup/agent-configs.d.ts +3 -2
  225. package/dist/setup/agent-writer.d.ts +3 -2
  226. package/dist/setup/approval-migration.d.ts +2 -1
  227. package/dist/setup/approval-migration.js +1 -1
  228. package/dist/setup/auto-approve.d.ts +11 -19
  229. package/dist/setup/auto-approve.js +15 -12
  230. package/dist/{init → setup}/confirm.js +1 -1
  231. package/dist/setup/desktop-shape.d.ts +3 -2
  232. package/dist/setup/dev-server-wait.d.ts +8 -0
  233. package/dist/setup/dev-server-wait.js +3 -1
  234. package/dist/setup/drive-agent.d.ts +2 -3
  235. package/dist/setup/drive-agent.js +1 -1
  236. package/dist/setup/drive-plan.d.ts +3 -2
  237. package/dist/setup/ensure-daemon.d.ts +2 -1
  238. package/dist/setup/existing-app.d.ts +12 -0
  239. package/dist/setup/existing-app.js +13 -0
  240. package/dist/setup/init-host.d.ts +11 -0
  241. package/dist/setup/init-host.js +26 -0
  242. package/dist/setup/init-runtime.d.ts +4 -10
  243. package/dist/setup/init-runtime.js +23 -16
  244. package/dist/setup/license-key.d.ts +2 -1
  245. package/dist/setup/listeners.d.ts +2 -1
  246. package/dist/setup/loopback-probe-urls.d.ts +18 -0
  247. package/dist/setup/loopback-probe-urls.js +52 -0
  248. package/dist/setup/node-effects.d.ts +3 -0
  249. package/dist/setup/node-effects.js +26 -11
  250. package/dist/setup/page-probe.d.ts +5 -0
  251. package/dist/setup/relaunch.d.ts +2 -1
  252. package/dist/setup/run-setup.d.ts +5 -1
  253. package/dist/setup/run-setup.js +155 -48
  254. package/dist/setup/setup-command.d.ts +3 -2
  255. package/dist/setup/setup-command.js +8 -0
  256. package/dist/setup/setup-options.d.ts +5 -3
  257. package/dist/setup/setup-options.js +3 -2
  258. package/dist/setup/wait-progress.d.ts +28 -0
  259. package/dist/setup/wait-progress.js +43 -0
  260. package/dist/telemetry/app-instrumented.d.ts +2 -1
  261. package/dist/telemetry/app-instrumented.js +1 -1
  262. package/dist/telemetry/automation-hint.d.ts +1 -1
  263. package/dist/telemetry/automation-hint.js +1 -1
  264. package/dist/telemetry/cli-telemetry.js +1 -1
  265. package/dist/telemetry/connect-failure.d.ts +1 -1
  266. package/dist/telemetry/connect-failure.js +1 -1
  267. package/dist/telemetry/crash-cause.d.ts +3 -2
  268. package/dist/telemetry/crash-cause.js +1 -1
  269. package/dist/telemetry/daemon-telemetry.d.ts +3 -2
  270. package/dist/telemetry/daemon-telemetry.js +1 -1
  271. package/dist/telemetry/error-class.d.ts +3 -2
  272. package/dist/telemetry/error-class.js +1 -1
  273. package/dist/telemetry/feedback-cli.js +1 -1
  274. package/dist/telemetry/feedback-context.d.ts +3 -2
  275. package/dist/telemetry/feedback-context.js +5 -4
  276. package/dist/telemetry/feedback-local.d.ts +3 -2
  277. package/dist/telemetry/feedback.d.ts +4 -3
  278. package/dist/telemetry/feedback.js +1 -1
  279. package/dist/telemetry/git-facts.d.ts +2 -2
  280. package/dist/telemetry/git-facts.js +1 -1
  281. package/dist/telemetry/identify.d.ts +1 -1
  282. package/dist/telemetry/identify.js +1 -1
  283. package/dist/telemetry/init-telemetry.d.ts +2 -14
  284. package/dist/telemetry/init-telemetry.js +4 -13
  285. package/dist/telemetry/install-source.d.ts +7 -13
  286. package/dist/telemetry/install-source.js +7 -29
  287. package/dist/telemetry/license-activation.d.ts +3 -2
  288. package/dist/telemetry/license-activation.js +1 -1
  289. package/dist/telemetry/machine-snapshot.d.ts +1 -1
  290. package/dist/telemetry/mcp-connection.js +1 -1
  291. package/dist/telemetry/project-profile.d.ts +0 -14
  292. package/dist/telemetry/project-profile.js +1 -1
  293. package/dist/telemetry/proxy-telemetry.js +1 -1
  294. package/dist/telemetry/session-metrics.d.ts +1 -1
  295. package/dist/telemetry/session-metrics.js +1 -1
  296. package/dist/telemetry/telemetry.d.ts +1 -17
  297. package/dist/telemetry/telemetry.js +4 -4
  298. package/dist/telemetry/tool-refused.d.ts +1 -1
  299. package/dist/telemetry/tool-refused.js +1 -1
  300. package/dist/tools/act-observation.d.ts +3 -2
  301. package/dist/tools/act-preflight.js +8 -3
  302. package/dist/tools/act-target.d.ts +4 -0
  303. package/dist/tools/act-target.js +14 -5
  304. package/dist/tools/act-tools.d.ts +0 -2
  305. package/dist/tools/act-tools.js +45 -15
  306. package/dist/tools/assert-source.d.ts +30 -0
  307. package/dist/tools/assert-source.js +21 -0
  308. package/dist/tools/assert-verdict.d.ts +1 -1
  309. package/dist/tools/assert-verdict.js +14 -4
  310. package/dist/tools/browser-tools.js +28 -8
  311. package/dist/tools/complete-snapshot.d.ts +3 -2
  312. package/dist/tools/dynamic-tools.js +8 -0
  313. package/dist/tools/error-recovery.d.ts +10 -0
  314. package/dist/tools/error-recovery.js +23 -0
  315. package/dist/tools/feedback-tools.js +1 -1
  316. package/dist/tools/invoke-tool.js +2 -0
  317. package/dist/tools/lease-tools.d.ts +48 -20
  318. package/dist/tools/lease-tools.js +311 -76
  319. package/dist/tools/lineage-tools.d.ts +14 -0
  320. package/dist/tools/lineage-tools.js +64 -0
  321. package/dist/tools/merge-tools.js +1 -3
  322. package/dist/tools/navigate-arrival.d.ts +45 -1
  323. package/dist/tools/navigate-arrival.js +39 -6
  324. package/dist/tools/navigate-result.d.ts +8 -1
  325. package/dist/tools/navigate-result.js +14 -4
  326. package/dist/tools/numeric-bounds.d.ts +0 -11
  327. package/dist/tools/numeric-bounds.js +4 -4
  328. package/dist/tools/observe-tools.js +22 -26
  329. package/dist/tools/observed-nothing.d.ts +9 -0
  330. package/dist/tools/observed-nothing.js +2 -1
  331. package/dist/tools/read-tools.js +12 -17
  332. package/dist/tools/real-input-attempt.d.ts +14 -6
  333. package/dist/tools/real-input-attempt.js +75 -18
  334. package/dist/tools/reconcile-tools.js +2 -1
  335. package/dist/tools/reload-result.d.ts +6 -1
  336. package/dist/tools/reload-result.js +7 -2
  337. package/dist/tools/resolve-target.d.ts +7 -1
  338. package/dist/tools/resolve-target.js +72 -9
  339. package/dist/tools/settle-in-flight.d.ts +1 -1
  340. package/dist/tools/settle-in-flight.js +9 -2
  341. package/dist/tools/snapshot-delta.d.ts +24 -1
  342. package/dist/tools/snapshot-delta.js +66 -7
  343. package/dist/tools/tool-names.d.ts +1 -0
  344. package/dist/tools/tool-names.js +1 -0
  345. package/dist/tools/tool-surface.js +20 -2
  346. package/dist/tools/tools.js +120 -11
  347. package/dist/tools/verify-next-baton.d.ts +2 -1
  348. package/dist/update/sdk-sync.d.ts +1 -1
  349. package/dist/update/sdk-sync.js +1 -1
  350. package/dist/update/updater.js +1 -1
  351. package/dist/version/sdk-fix.d.ts +2 -1
  352. package/dist/version/version-skew.d.ts +0 -8
  353. package/dist/version/version-skew.js +1 -1
  354. package/docs/README.md +8 -6
  355. package/docs/actions.mdx +1 -1
  356. package/docs/agent-cheatsheet.md +4 -2
  357. package/docs/architecture.md +1 -1
  358. package/docs/cli/mcp.mdx +3 -1
  359. package/docs/cli/watch.mdx +1 -1
  360. package/docs/cli.mdx +1 -1
  361. package/docs/desktop.mdx +1 -1
  362. package/docs/docs.json +8 -3
  363. package/docs/faq.mdx +9 -0
  364. package/docs/first-drive.md +136 -0
  365. package/docs/frameworks.mdx +29 -7
  366. package/docs/gate-plan.md +2 -2
  367. package/docs/gates.md +19 -11
  368. package/docs/getting-started.md +7 -1
  369. package/docs/http-transport.md +164 -0
  370. package/docs/index.mdx +5 -3
  371. package/docs/install-manual.mdx +1 -1
  372. package/docs/packages/tauri.mdx +1 -1
  373. package/docs/packages.mdx +15 -3
  374. package/docs/platform-integration.md +1 -1
  375. package/docs/predicates.mdx +2 -0
  376. package/docs/system-map.md +2 -2
  377. package/docs/telemetry-contract.md +4 -4
  378. package/docs/telemetry-events.mdx +1 -1
  379. package/docs/telemetry.md +3 -3
  380. package/docs/tools-navigate.mdx +14 -6
  381. package/docs/usage.md +4 -4
  382. package/package.json +7 -4
  383. package/dist/init/agent-rules.d.ts +0 -116
  384. package/dist/init/agent-rules.js +0 -279
  385. package/dist/init/app-choice.d.ts +0 -20
  386. package/dist/init/app-choice.js +0 -25
  387. package/dist/init/astro-host.d.ts +0 -21
  388. package/dist/init/astro-host.js +0 -27
  389. package/dist/init/astro-patch.d.ts +0 -33
  390. package/dist/init/astro-patch.js +0 -300
  391. package/dist/init/capabilities.d.ts +0 -40
  392. package/dist/init/capabilities.js +0 -150
  393. package/dist/init/closing-hint.d.ts +0 -36
  394. package/dist/init/closing-hint.js +0 -120
  395. package/dist/init/connect-steps.d.ts +0 -2
  396. package/dist/init/connect-steps.js +0 -50
  397. package/dist/init/cra.d.ts +0 -79
  398. package/dist/init/cra.js +0 -178
  399. package/dist/init/csp-check.d.ts +0 -38
  400. package/dist/init/csp-check.js +0 -107
  401. package/dist/init/csp-doctor.d.ts +0 -32
  402. package/dist/init/csp-doctor.js +0 -54
  403. package/dist/init/desktop-doctor.d.ts +0 -41
  404. package/dist/init/desktop-doctor.js +0 -198
  405. package/dist/init/detect.d.ts +0 -96
  406. package/dist/init/detect.js +0 -209
  407. package/dist/init/dev-script.d.ts +0 -50
  408. package/dist/init/dev-script.js +0 -66
  409. package/dist/init/format-generated.d.ts +0 -18
  410. package/dist/init/format-generated.js +0 -39
  411. package/dist/init/install-hint.d.ts +0 -9
  412. package/dist/init/install-hint.js +0 -29
  413. package/dist/init/mcp-clients.d.ts +0 -89
  414. package/dist/init/mcp-clients.js +0 -299
  415. package/dist/init/mcp-registered.d.ts +0 -12
  416. package/dist/init/mcp-registered.js +0 -14
  417. package/dist/init/mcp.d.ts +0 -89
  418. package/dist/init/mcp.js +0 -129
  419. package/dist/init/next-patch.d.ts +0 -39
  420. package/dist/init/next-patch.js +0 -196
  421. package/dist/init/node-io.d.ts +0 -7
  422. package/dist/init/node-io.js +0 -117
  423. package/dist/init/non-js-project.d.ts +0 -27
  424. package/dist/init/non-js-project.js +0 -84
  425. package/dist/init/patch-kind.d.ts +0 -20
  426. package/dist/init/patch-kind.js +0 -10
  427. package/dist/init/plan-framework.d.ts +0 -64
  428. package/dist/init/plan-framework.js +0 -565
  429. package/dist/init/plan.d.ts +0 -242
  430. package/dist/init/plan.js +0 -703
  431. package/dist/init/preflight.d.ts +0 -28
  432. package/dist/init/preflight.js +0 -38
  433. package/dist/init/project-id.d.ts +0 -12
  434. package/dist/init/project-id.js +0 -25
  435. package/dist/init/refresh-rules.d.ts +0 -39
  436. package/dist/init/refresh-rules.js +0 -51
  437. package/dist/init/run.d.ts +0 -117
  438. package/dist/init/run.js +0 -806
  439. package/dist/init/slash-command.d.ts +0 -27
  440. package/dist/init/slash-command.js +0 -132
  441. package/dist/init/snippets.d.ts +0 -134
  442. package/dist/init/snippets.js +0 -572
  443. package/dist/init/vite-config.d.ts +0 -18
  444. package/dist/init/vite-config.js +0 -114
  445. package/dist/init/windows-quote.d.ts +0 -38
  446. package/dist/init/windows-quote.js +0 -70
  447. package/dist/init/workspace-apps.d.ts +0 -30
  448. package/dist/init/workspace-apps.js +0 -178
  449. package/dist/project/remember-project.d.ts +0 -24
  450. package/dist/project/remember-project.js +0 -40
  451. /package/dist/{init → setup}/confirm.d.ts +0 -0
package/SKILL.md CHANGED
@@ -38,13 +38,9 @@ There are exactly three places you may stop, and none is a question about a pref
38
38
  2. **Your host asks the human to approve a command.** That prompt belongs to the host. Never bypass or suppress it, and take a refusal as the answer. `init` writing a pre-approval rule for the `reticle` server is not that: it is a scoped, announced config change the human asked for by running the command, and it covers only Reticle's own tools.
39
39
  3. **Only if no agent CLI exists on the machine at all**, the client restart that loads the MCP tools (step 2). If `claude`, `codex`, `cursor-agent` or `gemini` is installed, you do NOT stop. Step 2 shows you how to drive from a child process instead, and that path is measured to be the difference between a verdict and a dead end.
40
40
 
41
- If you do have to stop, the restart ends your turn. This file was fetched into the context that just ended, so nothing carries it across unless you make it. **Before you trigger the restart, write the remaining steps into `RETICLE-SETUP.md` at the repo root**, one line each, with the step you stopped at marked. That file is what you re-read afterwards. Delete it once step 5 has produced a verdict.
42
-
43
- Better still, install the on-disk copy first. It survives the restart on its own, and it is the same content:
41
+ **On Claude Code and Codex, do not ask the human to restart at all.** `npx @reticlehq/server init --relaunch` prints the exact resume command for this conversation; run that command yourself rather than handing the person a chore. It refuses when the id has no transcript behind it, because `--resume` on an empty id opens a blank session that looks exactly like success. Everywhere else the restart genuinely is theirs: name the one step for their client and ask for that alone.
44
42
 
45
- ```bash
46
- npx skills add reticlehq/reticle --skill install-and-verify -y
47
- ```
43
+ If you do have to stop, the restart ends your turn. This file was fetched into the context that just ended, so nothing carries it across unless you make it. **Before you trigger the restart, write the remaining steps into `RETICLE-SETUP.md` at the repo root**, one line each, with the step you stopped at marked. That file is what you re-read afterwards. Delete it once step 5 has produced a verdict.
48
44
 
49
45
  **After the restart, resume at the step you were on.** Do not start over, and do not skip forward.
50
46
 
@@ -89,11 +85,7 @@ Read **VERIFY** below when the question is "does this still work?" rather than "
89
85
 
90
86
  # SETUP
91
87
 
92
- **One command. It does all of it, and it ends with a verdict.**
93
-
94
- ```bash
95
- RETICLE_INSTALL_SOURCE=skill_file npx @reticlehq/server@latest init --flow "<the journey worth proving>"
96
- ```
88
+ **One command, the one at the top of this file. It does all of it, and it ends with a verdict.**
97
89
 
98
90
  `@latest` is deliberate: `npx` caches, and a stale cached CLI is the most common silent setup failure. Never pin a version here.
99
91
 
@@ -142,7 +134,7 @@ curl https://docs.reticle.sh/install-manual.md # register the MCP, wire the
142
134
  curl https://docs.reticle.sh/troubleshooting.md # nothing connected, click did nothing, verdict unknown
143
135
  ```
144
136
 
145
- **Vite + React**, **Next.js**, **Remix** and **Astro** are the frameworks with an app and a CI gate behind them, so their wiring is proven rather than assumed. The SDK is framework-agnostic and usually connects elsewhere, but say that honestly rather than reporting a proven install.
137
+ **Vite + React**, **Next.js**, **Remix** and **Astro** are the frameworks with an app and a CI gate behind them, so their wiring is proven rather than assumed. **TanStack Start** is detected and given a client-only recipe, but nothing in CI proves it. The SDK is framework-agnostic and usually connects elsewhere, but say that honestly rather than reporting a proven install.
146
138
 
147
139
  Do not reconstruct it from memory. Three things decide whether it works, and all three get skipped:
148
140
 
@@ -198,18 +190,18 @@ Work down this list and stop at the first row that fits. Do not hand-drive a flo
198
190
 
199
191
  | The question | The call | Calls |
200
192
  | --- | --- | --- |
201
- | "Did my edit break anything?" | `reticle_run({ tool: "reticle_verify", args: { action: "change", files: ["src/App.tsx"] } })` | 1 |
193
+ | "Did my edit break anything?" | `reticle_verify({ action: "change", files: ["src/App.tsx"] })` | 1 |
202
194
  | "Does this known journey still work?" | `reticle_run({ tool: "reticle_flow_replay", args: { flowName: "login" } })` | 1 |
203
195
  | "Does this new behaviour work?" | `reticle_act_sequence` for the setup, then ONE `reticle_act_and_wait` | 2 |
204
196
  | No MCP available at all | `npx @reticlehq/server verify <url>` in the shell | 1, no MCP |
205
197
 
206
- `reticle_verify` and `reticle_flow_replay` are **not on the advertised tool list**: they are reached through `reticle_run` exactly as written above. That is the supported call shape, not a workaround, and it is why you have to be told they exist at all.
198
+ `reticle_verify` IS on the advertised tool list, so call it directly. `reticle_flow_replay` is not: it is reached through `reticle_run` exactly as written above. That is the supported call shape, not a workaround, and it is why you have to be told it exists at all.
207
199
 
208
200
  `reticle_verify {action:"change"}` answers `unknown` when no saved flow covers the files you changed. That is the honest answer and not a failure. Nothing ran, so nothing was proved. It is also the signal to record one. Never read it as a pass.
209
201
 
210
202
  ## Two more you have to be told about
211
203
 
212
- Same story as `reticle_verify`: extended surface, so they are not in the tool list you were handed, and reached through `reticle_run`.
204
+ Extended surface, so they are not in the tool list you were handed, and reached through `reticle_run`.
213
205
 
214
206
  **Context compacted, a turn starting, or a sub-agent taking over?** Ask what this run already established, instead of re-snapshotting to rediscover what you already knew:
215
207
 
@@ -263,11 +255,9 @@ reticle_act_and_wait({ sessionId, ref, action: "click", until: { kind: "allOf",
263
255
 
264
256
  Prefer `reticle_act_and_wait({ ref, action, until })`. It names the expected consequence **before** the action, which is the difference between a check and a rationalisation.
265
257
 
266
- A verdict of `verified: "unknown"` is not a pass. It means Reticle drove the app and could not tell what happened. Report it as unknown. `verified: "no-fault"` is not a pass either. It means the page settled and no channel reported a problem, but nothing was declared to prove, so assert a consequence the action CHANGES. **Never weaken a check to make it pass.**
267
-
268
258
  Then report what you drove, what it produced, and the `file:line` for anything broken.
269
259
 
270
- The surface is deliberately small: `default` 18, `all` 30. Editors budget tools across every MCP server you have connected (Cursor allows 40 in total), so the count is capped rather than allowed to grow. `reticle_tools` loads the argument grammar for the rest on demand, and `reticle_run` invokes any of them by name. Nothing is unreachable; the cold tail just costs one discovery hop.
260
+ The surface is deliberately small: `default` 19, `all` 30. Editors budget tools across every MCP server you have connected (Cursor allows 40 in total), so the count is capped rather than allowed to grow. `reticle_tools` loads the argument grammar for the rest on demand, and `reticle_run` invokes any of them by name. Nothing is unreachable; the cold tail just costs one discovery hop.
271
261
 
272
262
  - Batching, regression suites, reading a verdict: `https://docs.reticle.sh/agent-cheatsheet.md`
273
263
  - Every predicate and action: `https://docs.reticle.sh/predicates.md`, `https://docs.reticle.sh/actions.md`
package/bin/reticle.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../dist/cli.js';
3
+ main();
@@ -26,7 +26,18 @@ const NO_TOKEN = 'authentication failed: no pairing token on the page';
26
26
  /** WebSocket close reasons are capped at 123 bytes; a longer one throws and closes with nothing. */
27
27
  const MAX_REASON_BYTES = 123;
28
28
  export function authFailureReason(servedProjects, helloProject, helloToken) {
29
- if (helloProject !== undefined && 0 < servedProjects.size && !servedProjects.has(helloProject)) {
29
+ // ONE served project, not merely some. A daemon that has accepted sessions from SEVERAL projects
30
+ // is demonstrably not owned by any one of them, so "that daemon is not yours, stop it" is the
31
+ // wrong story — and its advice is actively harmful there, because stopping it breaks the projects
32
+ // that are working. This is the normal shape for a globally-registered daemon, which several
33
+ // editors start from the user's home directory and point at everything.
34
+ //
35
+ // With one served project the inference holds and is worth making: the daemon belongs to that
36
+ // project and this HELLO is somebody else's. With several, the token is the suspect, and the plain
37
+ // reason says so without prescribing a recovery that costs other people their sessions.
38
+ if (helloProject !== undefined &&
39
+ 1 === servedProjects.size &&
40
+ !servedProjects.has(helloProject)) {
30
41
  const reason = `this daemon serves a different project — run \`reticle stop\` and retry`;
31
42
  return Buffer.byteLength(reason, 'utf8') <= MAX_REASON_BYTES ? reason : PLAIN;
32
43
  }
@@ -1,9 +1,17 @@
1
1
  import type { StartOptions } from '../index.js';
2
+ import { pairingTokenSource } from './pairing-token.js';
2
3
  /** The security contract the bridge/daemon enforce: bind host, pairing token, and WS origin allow-list. */
3
4
  interface BridgeSecurity {
4
5
  host?: string;
5
6
  token?: string;
6
7
  allowedOrigins?: string[];
8
+ /**
9
+ * Which source supplied `token`, decided HERE because this is the only place that knows.
10
+ * By the time the Bridge sees it, an auto-provisioned token and one passed by a caller are the
11
+ * same field — so computing it downstream would report `explicit` for every daemon and describe
12
+ * nothing. See pairingTokenSource for why a refusal needs this at all.
13
+ */
14
+ tokenSource?: ReturnType<typeof pairingTokenSource>;
7
15
  }
8
16
  /**
9
17
  * Resolve the bridge's security options from explicit options first, then the environment. Shared by
@@ -1,5 +1,5 @@
1
1
  import { ReticleEnv } from '@reticlehq/core';
2
- import { defaultPairingTokenDir, nodePairingTokenDeps, readOrCreatePairingToken, } from './pairing-token.js';
2
+ import { defaultPairingTokenDir, nodePairingTokenDeps, readOrCreatePairingToken, pairingTokenSource, } from './pairing-token.js';
3
3
  import { log } from '../log.js';
4
4
  /**
5
5
  * Resolve the bridge's security options from explicit options first, then the environment. Shared by
@@ -7,6 +7,10 @@ import { log } from '../log.js';
7
7
  * every entrypoint — a past divergence let daemon mode silently run with auth disabled.
8
8
  */
9
9
  export function resolveBridgeSecurity(options) {
10
+ const tokenSource = pairingTokenSource({
11
+ explicit: options.token !== undefined && 0 < options.token.length,
12
+ env: process.env,
13
+ });
10
14
  const envToken = process.env[ReticleEnv.TOKEN];
11
15
  const envOrigins = process.env[ReticleEnv.ALLOWED_ORIGINS];
12
16
  const host = options.host ?? process.env[ReticleEnv.HOST];
@@ -20,6 +24,7 @@ export function resolveBridgeSecurity(options) {
20
24
  ...(host === undefined ? {} : { host }),
21
25
  ...(token === undefined ? {} : { token }),
22
26
  ...(allowedOrigins === undefined ? {} : { allowedOrigins }),
27
+ tokenSource,
23
28
  };
24
29
  }
25
30
  /**
@@ -1,5 +1,6 @@
1
1
  import * as http from 'node:http';
2
2
  import { Session, SessionManager } from '../session/session.js';
3
+ import { pairingTokenSource } from './pairing-token.js';
3
4
  /** A human clicked ▶ on a saved flow in the panel — replay it with no agent. Wired by the daemon. */
4
5
  type ReplayRequestHandler = (sessionId: string, flowName: string) => void;
5
6
  /** Called once a browser session connects, so the daemon can push it the replayable-flow list. */
@@ -41,6 +42,8 @@ interface BridgeOptions {
41
42
  port: number;
42
43
  host?: string;
43
44
  token?: string;
45
+ /** Which source supplied `token`. Set by resolveBridgeSecurity; logged on a refusal, never sent. */
46
+ tokenSource?: ReturnType<typeof pairingTokenSource>;
44
47
  allowedOrigins?: string[];
45
48
  maxMessagesPerSecond?: number;
46
49
  maxSessions?: number;
@@ -5,8 +5,10 @@ import { WebSocketServer } from 'ws';
5
5
  import { EventType, HumanControlKind, RETICLE_WS_PATH, ReticleMessageSchema, LOOPBACK_HOST, MessageKind, ReticleEnv, RETICLE_PROTOCOL_VERSION, TRANSPORT_LIMITS, isLocalPage, isLoopbackHostname, isOpaqueOrigin, OPAQUE_ORIGIN, isHighValueEvent, RATE_CAP_HIGH_VALUE_RESERVE_RATIO, CONTRACT_FINGERPRINT, } from '@reticlehq/core';
6
6
  import { Session, SessionManager } from '../session/session.js';
7
7
  import { tokensMatch } from './token-auth.js';
8
+ import { pairingTokenSource } from './pairing-token.js';
8
9
  import { log } from '../log.js';
9
10
  import { getSessionMetrics } from '../telemetry/session-metrics.js';
11
+ import { sessionReplacedReason } from '../session/session-replaced.js';
10
12
  import { describeSkew, sdkFix, SkewPair } from '../version/version-skew.js';
11
13
  import { noteVersionSkew } from '../version/version-nudge.js';
12
14
  import { protocolSkewReason } from './protocol-skew.js';
@@ -174,6 +176,8 @@ export class Bridge {
174
176
  #wss;
175
177
  #clock;
176
178
  #token;
179
+ /** Which source supplied #token — logged on a refusal so a mismatch names its own cause. */
180
+ #tokenSource;
177
181
  /**
178
182
  * Projects this daemon has ACCEPTED a session from. Evidence, not derivation: it is what lets a
179
183
  * leaked daemon say "I belong to another project" instead of "authentication failed". See
@@ -223,6 +227,15 @@ export class Bridge {
223
227
  this.#clock = options.clock ?? (() => Date.now());
224
228
  this.#token =
225
229
  options.token !== undefined && options.token.length > 0 ? options.token : undefined;
230
+ // Supplied by resolveBridgeSecurity, which is the only place that can tell an auto-provisioned
231
+ // token from one a caller passed — both arrive here as options.token. Defaulted rather than
232
+ // required so a directly-constructed Bridge (tests, embedders) still logs something true.
233
+ this.#tokenSource =
234
+ options.tokenSource ??
235
+ pairingTokenSource({
236
+ explicit: options.token !== undefined && 0 < options.token.length,
237
+ env: process.env,
238
+ });
226
239
  // An entry that fails to normalize — typically a scheme-less `myapp.test` — used to be
227
240
  // filtered out with no trace: the allow-list LOOKED configured, every dial from that origin
228
241
  // was refused at the gate, and the refusal read as a scheme mismatch in the page rather than
@@ -446,10 +459,16 @@ export class Bridge {
446
459
  // token is not wrong, it is somebody else's — and "authentication failed" sends the user to
447
460
  // check the one thing that is fine. See auth-failure-reason.
448
461
  const reason = authFailureReason(this.#servedProjects, parsed.projectId, parsed.token);
462
+ // The token SOURCE, never the token. A refusal here has no other witness: the value is
463
+ // machine-level on both sides, so the daemon's cwd cannot explain a mismatch (#685) and
464
+ // the only thing that can is the environment the two processes were started in — which
465
+ // differs exactly when an IDE spawns the MCP server globally instead of from the shell
466
+ // that launched the dev server.
449
467
  log('authentication_failed', {
450
468
  served: [...this.#servedProjects],
451
469
  ...(parsed.projectId === undefined ? {} : { helloProject: parsed.projectId }),
452
470
  presented: parsed.token !== undefined && 0 < parsed.token.length,
471
+ tokenSource: this.#tokenSource,
453
472
  });
454
473
  this.sessions.noteClosure(WS_CLOSE_REASON.AUTH_FAILED, this.#clock());
455
474
  socket.close(WS_CLOSE.AUTH_FAILED[0], reason);
@@ -509,7 +528,7 @@ export class Bridge {
509
528
  // the old handle can finish against the live connection instead of returning an error whose
510
529
  // only answer is to go and rediscover an id that has not changed. See Session.succeededBy.
511
530
  replaced.succeededBy(session);
512
- replaced.disconnect(`session replaced by a newer connection claiming the same id (${session.id}) from ${session.url}`, true);
531
+ replaced.disconnect(sessionReplacedReason(session.id, session.url), true);
513
532
  }
514
533
  // The daemon is the single judge of skew, and HELLO is where the page announces itself.
515
534
  // Reported on the session (reticle_sessions) AND queued for the next tool result, because an
@@ -520,6 +539,9 @@ export class Bridge {
520
539
  contract: parsed.contract,
521
540
  fix: this.#sdkFix(),
522
541
  }, { version: SERVER_VERSION, contract: CONTRACT_FINGERPRINT });
542
+ // Kept so a remedy can check whether it applies to THIS page — see body-capture-remedy.
543
+ session.sdkVersion = parsed.sdkVersion;
544
+ session.captureBodies = parsed.captureBodies;
523
545
  if (skew !== undefined) {
524
546
  log('version_skew', {
525
547
  sessionId: session.id,
@@ -27,10 +27,19 @@ export declare class FakeBrowser {
27
27
  name: string;
28
28
  args: Record<string, unknown>;
29
29
  }[];
30
- /** The snapshot tree to return. Mutable so tests can vary the page between calls. */
30
+ /**
31
+ * The snapshot tree to return. Mutable so tests can vary the page between calls.
32
+ *
33
+ * The counters are optional because the fields they stand for are: the browser omits `nodes`-
34
+ * adjacent diagnostics on a page that has nothing to report, and a test asserting on one has to be
35
+ * able to send a payload shaped like the real thing.
36
+ */
31
37
  snapshotResult: {
32
38
  tree: string;
33
39
  status: Record<string, unknown>;
40
+ nodes?: number;
41
+ hiddenSkipped?: number;
42
+ leanSkipped?: number;
34
43
  };
35
44
  constructor(port: number, sessionId: string, hasCapabilities?: boolean);
36
45
  open(): Promise<void>;
@@ -34,7 +34,13 @@ export class FakeBrowser {
34
34
  queryResolves = true;
35
35
  /** Records every command the bridge sent (for replay assertions). */
36
36
  received = [];
37
- /** The snapshot tree to return. Mutable so tests can vary the page between calls. */
37
+ /**
38
+ * The snapshot tree to return. Mutable so tests can vary the page between calls.
39
+ *
40
+ * The counters are optional because the fields they stand for are: the browser omits `nodes`-
41
+ * adjacent diagnostics on a page that has nothing to report, and a test asserting on one has to be
42
+ * able to send a payload shaped like the real thing.
43
+ */
38
44
  snapshotResult = {
39
45
  tree: '- button "Pay" (ref=e7)\n- dialog "Order confirmed" (ref=e12)',
40
46
  status: { route: '/checkout' },
@@ -16,6 +16,23 @@ export interface PairingTokenDeps {
16
16
  export declare const PAIRING_TOKEN_DIR_ENV: "RETICLE_PAIRING_TOKEN_DIR";
17
17
  /** The default directory that holds the token — the per-user ~/.reticle so it's shared across projects. */
18
18
  export declare function defaultPairingTokenDir(): string;
19
+ /**
20
+ * Which of the three sources supplied the token this process is using.
21
+ *
22
+ * The SOURCE, never the secret. #685 reports a globally-registered daemon refusing an app that
23
+ * `doctor` calls correctly wired, and blames the daemon's cwd — but cwd cannot reach this value:
24
+ * resolution is options.token, then RETICLE_TOKEN, then the file under ~/.reticle, and the Vite
25
+ * plugin reads that same file. What CAN diverge is the ENVIRONMENT, because an IDE spawning a global
26
+ * MCP server does not inherit the shell that started the dev server. Either variable then moves one
27
+ * side and not the other, and neither is visible in anything we print — so the refusal is a mystery
28
+ * with no witness, which is how this report has stayed open on a guessed cause.
29
+ *
30
+ * Pure, and takes its env, so the order can be pinned by tests rather than by reading two files.
31
+ */
32
+ export declare function pairingTokenSource(input: {
33
+ explicit: boolean;
34
+ env: NodeJS.ProcessEnv;
35
+ }): 'explicit' | 'env:RETICLE_TOKEN' | 'env:RETICLE_PAIRING_TOKEN_DIR' | 'default';
19
36
  export declare function pairingTokenPath(dir: string): string;
20
37
  /** Production IO adapter: 0600 file + 0700 dir so only the owner can read the secret. */
21
38
  export declare function nodePairingTokenDeps(): PairingTokenDeps;
@@ -15,6 +15,32 @@ export function defaultPairingTokenDir() {
15
15
  return override;
16
16
  return join(homedir(), ReticleDir.ROOT);
17
17
  }
18
+ /**
19
+ * Which of the three sources supplied the token this process is using.
20
+ *
21
+ * The SOURCE, never the secret. #685 reports a globally-registered daemon refusing an app that
22
+ * `doctor` calls correctly wired, and blames the daemon's cwd — but cwd cannot reach this value:
23
+ * resolution is options.token, then RETICLE_TOKEN, then the file under ~/.reticle, and the Vite
24
+ * plugin reads that same file. What CAN diverge is the ENVIRONMENT, because an IDE spawning a global
25
+ * MCP server does not inherit the shell that started the dev server. Either variable then moves one
26
+ * side and not the other, and neither is visible in anything we print — so the refusal is a mystery
27
+ * with no witness, which is how this report has stayed open on a guessed cause.
28
+ *
29
+ * Pure, and takes its env, so the order can be pinned by tests rather than by reading two files.
30
+ */
31
+ export function pairingTokenSource(input) {
32
+ // Same precedence the resolver applies, deliberately: a source that did not decide the value is a
33
+ // misdirection, and this exists to stop one.
34
+ if (input.explicit)
35
+ return 'explicit';
36
+ const token = input.env[ReticleEnv.TOKEN];
37
+ if (token !== undefined && 0 < token.length)
38
+ return `env:${ReticleEnv.TOKEN}`;
39
+ const dir = input.env[PAIRING_TOKEN_DIR_ENV];
40
+ if (dir !== undefined && 0 < dir.length)
41
+ return `env:${PAIRING_TOKEN_DIR_ENV}`;
42
+ return 'default';
43
+ }
18
44
  export function pairingTokenPath(dir) {
19
45
  return join(dir, ReticleDir.PAIRING_TOKEN_FILE);
20
46
  }
@@ -1,4 +1,11 @@
1
1
  import type { IncomingMessage } from 'node:http';
2
+ /**
3
+ * The query parameter a client may present its pairing token in.
4
+ *
5
+ * Exported because the CLI now presents the same token when it attaches to a running daemon over the
6
+ * MCP HTTP transport, and a second spelling of this string is a second way for auth to fail.
7
+ */
8
+ export declare const TOKEN_QUERY_PARAM = "token";
2
9
  /**
3
10
  * Timing-safe pairing-token comparison. Length-guards first because `timingSafeEqual` throws on a
4
11
  * length mismatch — and the guard itself is not a timing oracle (length is not the secret).
@@ -2,7 +2,13 @@ import { timingSafeEqual } from 'node:crypto';
2
2
  import { isLoopbackHostname } from '@reticlehq/core';
3
3
  /** `Authorization: Bearer <token>` prefix and the query-param fallback for clients that can't set headers. */
4
4
  const BEARER_PREFIX = 'Bearer ';
5
- const TOKEN_QUERY_PARAM = 'token';
5
+ /**
6
+ * The query parameter a client may present its pairing token in.
7
+ *
8
+ * Exported because the CLI now presents the same token when it attaches to a running daemon over the
9
+ * MCP HTTP transport, and a second spelling of this string is a second way for auth to fail.
10
+ */
11
+ export const TOKEN_QUERY_PARAM = 'token';
6
12
  /** Node reports IPv4 loopback over a dual-stack socket as this mapped form; strip it before classifying. */
7
13
  const IPV4_MAPPED_PREFIX = /^::ffff:/i;
8
14
  /**
@@ -31,19 +31,19 @@ declare const CapsuleSchema: z.ZodObject<{
31
31
  /** Minimal steps to reproduce (prefix-trimmed). */
32
32
  steps: z.ZodArray<z.ZodType<FlowStep, z.ZodTypeDef, FlowStep>, "many">;
33
33
  }, "strip", z.ZodTypeAny, {
34
+ origin: string;
35
+ expected: string;
34
36
  id: string;
35
37
  version: 1;
36
- expected: string;
37
- origin: string;
38
38
  steps: FlowStep[];
39
39
  observed: string;
40
40
  createdAt: number;
41
41
  flow?: string | undefined;
42
42
  }, {
43
+ origin: string;
44
+ expected: string;
43
45
  id: string;
44
46
  version: 1;
45
- expected: string;
46
- origin: string;
47
47
  steps: FlowStep[];
48
48
  observed: string;
49
49
  createdAt: number;
@@ -15,4 +15,4 @@ export interface DivergenceCapsule {
15
15
  blastRadius: string[];
16
16
  }
17
17
  /** Compose the full capsule from the declared chain and the observed (attributed) event window. */
18
- export declare function buildDivergenceCapsule(expected: readonly ExpectedLink[], observed: readonly ReticleEvent[]): DivergenceCapsule;
18
+ export declare function buildDivergenceCapsule(expected: readonly ExpectedLink[], observed: readonly ReticleEvent[], truncated?: boolean): DivergenceCapsule;
@@ -26,10 +26,10 @@ function blastRadius(expected, observed) {
26
26
  return radius;
27
27
  }
28
28
  /** Compose the full capsule from the declared chain and the observed (attributed) event window. */
29
- export function buildDivergenceCapsule(expected, observed) {
29
+ export function buildDivergenceCapsule(expected, observed, truncated = false) {
30
30
  return {
31
- summary: causalSummary(observed),
32
- firstDivergence: firstDivergence(expected, observed),
31
+ summary: causalSummary(observed, { truncated }),
32
+ firstDivergence: firstDivergence(expected, observed, truncated),
33
33
  blastRadius: blastRadius(expected, observed),
34
34
  };
35
35
  }
@@ -76,10 +76,30 @@ export interface CausalSummary {
76
76
  * `stateSettleMs` and `elided` follow.
77
77
  */
78
78
  stateUnwatched?: true;
79
+ /**
80
+ * TRUE when the ring buffer evicted events belonging to this window, so every count and list here
81
+ * is a FLOOR, not a total — `net.total: 0` means "none survived", never "none happened".
82
+ *
83
+ * The same distinction `stateUnwatched` draws, from the other cause, and it was the more expensive
84
+ * one. A field app rewrote one localStorage key thousands of times a minute; the no-op writes
85
+ * starved the buffer (`held: 2000, dropped: 70482`), and the verdict for that window reported
86
+ * `net.total: 0`, `stateDiffs: []` and "state never changed" — while a POST that had returned 200
87
+ * in that exact window carried the entire root cause in its body. The agent read the zero and was
88
+ * about to report "clicking Accept fires no network request", which points a developer at the
89
+ * click handler instead of at the payload the server rejected.
90
+ *
91
+ * `honesty.integrity.losses` already said `buffer_loss`. It was not enough: the headline fields an
92
+ * agent reads to form a verdict are these, and a bare `0` several levels above a nested integrity
93
+ * flag reads as a fact. The absolute and the caveat must not be separable, so the caveat rides on
94
+ * the summary that carries the absolutes.
95
+ */
96
+ truncated?: true;
79
97
  }
80
98
  /** What only the SESSION knows — level facts the event window cannot contain. See `stateUnwatched`. */
81
- export interface SummaryContext {
99
+ interface SummaryContext {
82
100
  stateUnwatched?: boolean;
101
+ /** Did the buffer drop events from this window — see `CausalSummary.truncated`. */
102
+ truncated?: boolean;
83
103
  }
84
104
  /**
85
105
  * THE INVARIANT: every field of this summary has a bounded size that does not depend on how much the
@@ -1,5 +1,5 @@
1
1
  import { EventType, PerfMetric, isDevToolingUrl } from '@reticlehq/core';
2
- import { routePathOf } from '../events/predicate-route.js';
2
+ import { routeOfEvent } from '../events/predicate-route.js';
3
3
  /**
4
4
  * THE INVARIANT: every field of this summary has a bounded size that does not depend on how much the
5
5
  * app did. A verdict travels in the same payload as its evidence, so an unbounded evidence field is
@@ -119,14 +119,15 @@ export function causalSummary(events, context = {}) {
119
119
  }
120
120
  break;
121
121
  }
122
- case EventType.ROUTE_CHANGE:
122
+ case EventType.ROUTE_CHANGE: {
123
123
  // The ROUTER's path. Reporting the document pathname left `route: "/"` on every action of a
124
124
  // hash-routed app — the same reading that made a route predicate unsatisfiable there, and an
125
- // agent reasoning off this field would conclude the route never changed. See routePathOf.
126
- if ('string' === typeof data['pathname']) {
127
- route = routePathOf(data['pathname'], 'string' === typeof data['hash'] ? data['hash'] : '');
128
- }
125
+ // agent reasoning off this field would conclude the route never changed. See routeOfEvent.
126
+ const routed = routeOfEvent(data);
127
+ if (routed !== undefined)
128
+ route = routed.routePath;
129
129
  break;
130
+ }
130
131
  case EventType.SIGNAL:
131
132
  pushUnique(signals, data['name']);
132
133
  break;
@@ -166,5 +167,6 @@ export function causalSummary(events, context = {}) {
166
167
  ...(layoutShift === undefined ? {} : { layoutShift }),
167
168
  longTasks,
168
169
  ...(true === context.stateUnwatched ? { stateUnwatched: true } : {}),
170
+ ...(true === context.truncated ? { truncated: true } : {}),
169
171
  };
170
172
  }
@@ -32,4 +32,4 @@ export interface Divergence {
32
32
  * Walk the declared chain in order; return the first link not satisfied by the observed events (with a
33
33
  * side-by-side of what happened instead), or null when the whole chain held.
34
34
  */
35
- export declare function firstDivergence(expected: readonly ExpectedLink[], observed: readonly ReticleEvent[]): Divergence | null;
35
+ export declare function firstDivergence(expected: readonly ExpectedLink[], observed: readonly ReticleEvent[], truncated?: boolean): Divergence | null;
@@ -20,26 +20,42 @@ function satisfies(link, events) {
20
20
  (link.status === undefined || e.data['status'] === link.status));
21
21
  }
22
22
  }
23
- /** Describe what WAS observed for a link that failed — the closest attempt, for a side-by-side. */
24
- function observedFor(link, events) {
23
+ /**
24
+ * Describe what WAS observed for a link that failed — the closest attempt, for a side-by-side.
25
+ *
26
+ * `truncated` says the buffer dropped events belonging to this window, which turns every sentence
27
+ * below from a claim about the app into a claim about a partial reading. It is the difference
28
+ * between "the request was never made" and "no surviving event shows the request", and a field
29
+ * session proved they are not interchangeable: a POST that returned 200 inside the window carried
30
+ * the whole root cause, and the capsule for that same window said `state "cad" never changed`. The
31
+ * agent believed the absolute — the strongest, most specific, and most wrong thing this tool can
32
+ * say. Absence of evidence must never be typed as evidence of absence.
33
+ */
34
+ function observedFor(link, events, truncated) {
25
35
  if (ConsequenceKind.NET === link.kind) {
26
36
  const match = net(events).find((e) => 'string' === typeof e.data['url'] && e.data['url'].includes(link.urlContains));
27
- if (match === undefined)
28
- return `no request to ${link.urlContains}`;
29
- return `${link.urlContains} responded ${String(match.data['status'])} (expected ${String(link.status)})`;
37
+ if (match !== undefined) {
38
+ return `${link.urlContains} responded ${String(match.data['status'])} (expected ${String(link.status)})`;
39
+ }
40
+ return truncated
41
+ ? `no surviving event shows a request to ${link.urlContains} — capture truncated, so this is not evidence there was none`
42
+ : `no request to ${link.urlContains}`;
30
43
  }
31
- if (ConsequenceKind.SIGNAL === link.kind)
32
- return `signal "${link.name}" never fired`;
33
- return `state "${link.name}" never changed`;
44
+ const subject = ConsequenceKind.SIGNAL === link.kind ? `signal "${link.name}"` : `state "${link.name}"`;
45
+ if (truncated)
46
+ return `${subject} was not observed — capture truncated, so absence is unproven`;
47
+ return ConsequenceKind.SIGNAL === link.kind
48
+ ? `${subject} never fired`
49
+ : `${subject} never changed`;
34
50
  }
35
51
  /**
36
52
  * Walk the declared chain in order; return the first link not satisfied by the observed events (with a
37
53
  * side-by-side of what happened instead), or null when the whole chain held.
38
54
  */
39
- export function firstDivergence(expected, observed) {
55
+ export function firstDivergence(expected, observed, truncated = false) {
40
56
  for (const link of expected) {
41
57
  if (!satisfies(link, observed)) {
42
- return { expected: link, observed: observedFor(link, observed) };
58
+ return { expected: link, observed: observedFor(link, observed, truncated) };
43
59
  }
44
60
  }
45
61
  return null;
@@ -17,7 +17,7 @@
17
17
  */
18
18
  export declare const CHROMIUM_ANTI_THROTTLING_ARGS: readonly ["--disable-background-timer-throttling", "--disable-backgrounding-occluded-windows", "--disable-renderer-backgrounding"];
19
19
  /** The options object handed to `chromium.launch` at both launch sites. */
20
- export interface ChromiumLaunchOptions {
20
+ interface ChromiumLaunchOptions {
21
21
  headless: boolean;
22
22
  args: string[];
23
23
  }
@@ -26,3 +26,4 @@ export interface ChromiumLaunchOptions {
26
26
  * each call so no caller can mutate the constant behind everyone else's back.
27
27
  */
28
28
  export declare function chromiumLaunchOptions(headless: boolean): ChromiumLaunchOptions;
29
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * A verdict from the CLI, against the daemon that is already running.
3
+ *
4
+ * THE DEAD END THIS REMOVES. The largest single cluster in the field feedback is not a defect in any
5
+ * tool — it is that the tools were not loaded in the client, and there was no supported way to reach
6
+ * a verdict from that state. The shape was always the same: the app is instrumented, the daemon is
7
+ * healthy, `doctor` shows a live connected page, and the client (Codex, Cursor Cloud, Antigravity,
8
+ * Gemini CLI, or a Claude Code session whose MCP link dropped) exposes no `reticle_*` tools, because
9
+ * they load only at client startup. The documented CLI fallback then refused, because the daemon
10
+ * already owned the port — precisely the state a successful install leaves you in. The other CLI
11
+ * verdict paths need saved flows, and a first-install project has none. And our own guidance says
12
+ * never to stop the daemon, because that kills the agent's MCP link.
13
+ *
14
+ * So an agent held a live, correctly-wired app and no path to a verdict short of a human restarting
15
+ * their editor. Several reporters fell back to Playwright or to their own client and said honestly
16
+ * that Reticle could not produce a verdict.
17
+ *
18
+ * HOW, AND WHY IT ADDS NO NEW SURFACE. One reporter found the answer themselves: they drove the full
19
+ * tool surface over the daemon's HTTP/SSE transport with a hand-written client, and it worked. That
20
+ * transport is not opt-in — `start()` calls `attachMcp` unconditionally, so every daemon has it. So
21
+ * this asks the running daemon the same questions an agent would, over the same transport, using the
22
+ * MCP client the SDK already ships. Nothing new is exposed and nothing is bound: the daemon keeps
23
+ * the port, the agent keeps its link, and there is nothing to stop.
24
+ *
25
+ * It is deliberately the CLI equivalent of ONE `act_and_wait`: navigate, then assert. A CLI that
26
+ * grew its own scripting language would be a second way to express a journey, competing with flows
27
+ * and drifting from them.
28
+ */
29
+ /** What a one-shot verdict reports back to the caller. */
30
+ export interface AdhocVerdict {
31
+ /** Process exit code: 0 only when the predicate was PROVED. */
32
+ code: number;
33
+ /** Lines to print, in order. */
34
+ lines: string[];
35
+ }
36
+ export interface AdhocVerdictOptions {
37
+ port: number;
38
+ /** Navigated to first when given; omitted to assert against wherever the session already is. */
39
+ url?: string;
40
+ /** The predicate, already parsed from the caller's JSON. */
41
+ predicate: unknown;
42
+ token?: string;
43
+ /** Injected so a test does not need a live daemon. */
44
+ connect?: (endpoint: URL) => Promise<ToolCaller>;
45
+ }
46
+ /** The narrow slice of an MCP client this needs — one call, then close. */
47
+ export interface ToolCaller {
48
+ call(name: string, args: Record<string, unknown>): Promise<unknown>;
49
+ close(): Promise<void>;
50
+ }
51
+ /**
52
+ * Navigate (optionally) and assert, against the daemon already on this port.
53
+ *
54
+ * Exit code 0 ONLY for `verified: "yes"`. `unknown` exits non-zero on purpose: it means Reticle could
55
+ * not tell what happened, and a CI step that treats "could not tell" as success is the false green
56
+ * this whole product exists to prevent.
57
+ */
58
+ export declare function runAdhocVerdict(options: AdhocVerdictOptions): Promise<AdhocVerdict>;