@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
@@ -1,4 +1,61 @@
1
- import { BrowserBrand, BUFFER_EVICTION_WARNING, THROTTLED_WARNING } from '@reticlehq/core';
1
+ import { BrowserBrand, EventType, BUFFER_EVICTION_WARNING, THROTTLED_STARVED_NOTE, THROTTLED_WARNING, } from '@reticlehq/core';
2
+ /**
3
+ * How long a request may be in flight before it is worth reporting on session health.
4
+ *
5
+ * A drive always has requests in the air, so a low bar puts a scary number on every healthy session
6
+ * and trains agents to ignore the field — the same way a guard that fires on routine controls trains
7
+ * them to pass `confirmDangerous` reflexively.
8
+ *
9
+ * 10s, and it is a HEURISTIC with no clean separation available: "a slow endpoint" and "a server
10
+ * that will never answer" are the same observation until one of them finishes. The number is chosen
11
+ * to sit between the two things actually measured, not derived from anything.
12
+ *
13
+ * Below it: this repo's own battery runs its API with `REFLECT_MS=6000` on purpose, and at the 5s
14
+ * this first shipped with, a healthy app with one slow route was permanently non-nominal and carried
15
+ * a scary number on every result.
16
+ *
17
+ * Above it: both reported wedges — 10.4s on one, 170s+ on the other. 10.4s clears this by 400ms,
18
+ * which is thin, and an app with an 8-second endpoint will not be warned about a wedge until 10s.
19
+ * That is the trade: an over-firing field gets ignored, and a field nobody reads catches nothing.
20
+ */
21
+ export const PENDING_NAVIGATION_NOTICE_MS = 10_000;
22
+ /**
23
+ * Said beside `dispatched` / `settled` on a skewed session, because those fields read as success.
24
+ *
25
+ * The reporter's own account of the cost: "the false-positive dispatch confirmation is what cost the
26
+ * most time — I trusted `dispatched: true, domMutatedWithin: Nms` as real signal for a long time
27
+ * before suspecting the skew banner." Stating the contradiction is the whole job here; the existing
28
+ * banner was accurate and simply never appeared where it was needed.
29
+ */
30
+ const SKEW_VERDICT_WARNING = 'version skew: `dispatched` and `settled` on this session mean the event was sent, NOT that the ' +
31
+ 'app acted on it — a skewed pair drops actions silently. Re-read the control (aria-checked, ' +
32
+ 'data-state, its text) before trusting any verdict from this session, and converge the versions:';
33
+ /**
34
+ * The age of the oldest request that STARTED and never completed, or undefined when there is none
35
+ * worth reporting.
36
+ *
37
+ * Matched on request id, the way `detectHungRequests` and the route oracle's `unansweredIn` do: a
38
+ * NET_PENDING with no NET_REQUEST carrying the same id. Three readings of one fact is two too many,
39
+ * but they read different windows — this one is the whole session, which is the point: a wedge that
40
+ * began before the current action is exactly the case a per-window reading cannot see.
41
+ */
42
+ export function pendingNavigationMs(events, nowMs) {
43
+ const settled = new Set();
44
+ for (const e of events)
45
+ if (e.type === EventType.NET_REQUEST)
46
+ settled.add(e.data['id']);
47
+ let oldest;
48
+ for (const e of events) {
49
+ if (e.type !== EventType.NET_PENDING || settled.has(e.data['id']))
50
+ continue;
51
+ if (oldest === undefined || e.t < oldest)
52
+ oldest = e.t;
53
+ }
54
+ if (oldest === undefined)
55
+ return undefined;
56
+ const age = nowMs - oldest;
57
+ return age >= PENDING_NAVIGATION_NOTICE_MS ? age : undefined;
58
+ }
2
59
  /**
3
60
  * Buffer-honesty envelope for observe/network/console. When the ring buffer has evicted anything
4
61
  * (age/size cap), a "no such event" answer may be a false negative — so we attach the drop count and
@@ -20,9 +77,21 @@ export function bufferEnvelope(session) {
20
77
  */
21
78
  export function healthEnvelope(session) {
22
79
  const health = session.health();
23
- const nominal = !health.throttled && health.focused && health.recommendation === undefined;
80
+ // A request stuck in flight makes a session non-nominal even when the tab is fine, because it is
81
+ // the one condition where every observation is about to be about a page that is not moving.
82
+ const nominal = !health.throttled &&
83
+ health.focused &&
84
+ health.recommendation === undefined &&
85
+ health.pendingNavigationMs === undefined &&
86
+ health.versionSkew === undefined;
24
87
  if (nominal)
25
88
  return {};
89
+ // Skew outranks the throttle warning when both hold: a throttled tab makes a reading unreliable,
90
+ // a skewed link makes the ACTION unreliable, and there is no point warning about the quality of an
91
+ // observation of something that may never have happened.
92
+ if (health.versionSkew !== undefined) {
93
+ return { session: health, warning: `${SKEW_VERDICT_WARNING} ${health.versionSkew}` };
94
+ }
26
95
  return health.throttled ? { session: health, warning: THROTTLED_WARNING } : { session: health };
27
96
  }
28
97
  /**
@@ -50,12 +119,23 @@ const STARVED_WAIT_NOTE = ' (the tab was throttled during this wait, so starvati
50
119
  * Suffix the starvation note onto a FAILED predicate verdict when the tab is throttled. A pass is
51
120
  * returned untouched — starvation is context for a failure, not a reason to doubt a hold. Pure and
52
121
  * generic over the verdict shape so assert/wait_for/act_and_wait share one rule.
122
+ *
123
+ * The sentence FOLLOWS the field, and does not decide for itself. `annotateThrottledMiss`
124
+ * (predicate.ts) owns the question of whether this failure was reached by not having seen
125
+ * something, which is the only reading a starved tab casts doubt on; it stamps
126
+ * `THROTTLED_STARVED_NOTE` when it was. Deciding again here got two answers to one question:
127
+ * an `absent: true` assertion that MATCHED 13 elements was graded honestly in the field an agent
128
+ * gates on, and told in prose that the tab may never have rendered. It also doubled up on a failure
129
+ * that already carried a more specific reason (an unreadable locator, a superseded window), where
130
+ * the concrete diagnosis is the one that should lead.
53
131
  */
54
132
  export function annotateStarvedFailure(session, verdict) {
55
133
  if (true === verdict.pass || verdict.failureReason === undefined)
56
134
  return verdict;
57
135
  if (true !== session.throttled())
58
136
  return verdict;
137
+ if (THROTTLED_STARVED_NOTE !== verdict.inconclusive)
138
+ return verdict;
59
139
  return { ...verdict, failureReason: `${verdict.failureReason}${STARVED_WAIT_NOTE}` };
60
140
  }
61
141
  /**
@@ -1,7 +1,8 @@
1
- import type { NoSessionReason } from '@reticlehq/core';
1
+ import type { NoSessionReason } from '@reticlehq/core/telemetry';
2
2
  import { Session, type SessionInfo } from './session.js';
3
3
  import { AttachmentHistory } from './attachment-history.js';
4
4
  import type { NoSessionNextAction } from './no-session-next-action.js';
5
+ import { type SessionIdentity } from './session-successor.js';
5
6
  /**
6
7
  * The agent's active project, used to scope auto-selection. `projectId` is the stable build-stamped
7
8
  * identity (authoritative when present); `url` is the app origin, a fallback hint for older SDKs that
@@ -61,6 +62,15 @@ export declare class SessionManager {
61
62
  * vanished rather than about the daemon's lifetime history (#611).
62
63
  */
63
64
  lastDeparted(): string | undefined;
65
+ /**
66
+ * The session that most recently disappeared — id, last URL, project — or undefined.
67
+ *
68
+ * `lastDeparted` is the id alone, which is what lease-expiry attribution needs. This is the
69
+ * rest: a route that 500s tears the page down and the SDK never reconnects, and without the
70
+ * URL `reticle_sessions` is an empty list with no history (#808). Tombstones, not live rows:
71
+ * a connected tab is in `list()`, not here.
72
+ */
73
+ lastKnown(): SessionIdentity | undefined;
64
74
  resolve(sessionId?: string, scope?: ResolveScope): Session;
65
75
  }
66
76
  export {};
@@ -303,9 +303,20 @@ export class SessionManager {
303
303
  * vanished rather than about the daemon's lifetime history (#611).
304
304
  */
305
305
  lastDeparted() {
306
+ return this.lastKnown()?.id;
307
+ }
308
+ /**
309
+ * The session that most recently disappeared — id, last URL, project — or undefined.
310
+ *
311
+ * `lastDeparted` is the id alone, which is what lease-expiry attribution needs. This is the
312
+ * rest: a route that 500s tears the page down and the SDK never reconnects, and without the
313
+ * URL `reticle_sessions` is an empty list with no history (#808). Tombstones, not live rows:
314
+ * a connected tab is in `list()`, not here.
315
+ */
316
+ lastKnown() {
306
317
  let last;
307
- for (const id of this.#tombstones.keys())
308
- last = id;
318
+ for (const record of this.#tombstones.values())
319
+ last = record;
309
320
  return last;
310
321
  }
311
322
  /**
@@ -324,8 +335,12 @@ export class SessionManager {
324
335
  #unknownSessionError(sessionId) {
325
336
  const live = [...this.#sessions.values()];
326
337
  if (0 === live.length) {
338
+ const known = this.lastKnown();
339
+ const where = undefined === known ? '' : ` Last seen on ${known.url}.`;
327
340
  return (`no connected session with id '${sessionId}', and no sessions are connected at all — so ` +
328
- 'there is no other id to retry with. The app is not dialling this daemon; call ' +
341
+ 'there is no other id to retry with.' +
342
+ where +
343
+ ' The app is not dialling this daemon; call ' +
329
344
  'reticle_sessions for the diagnosis rather than retrying this call.');
330
345
  }
331
346
  const named = live.map((s) => `'${s.id}' (${s.url})`).join(', ');
@@ -0,0 +1,24 @@
1
+ /**
2
+ * "The page reconnected under the same id" — one sentence, and one way to recognise it.
3
+ *
4
+ * A server-rendered app reconnects its SDK on EVERY full page load, so on an MPA this is the normal
5
+ * consequence of following a link, not a fault. The bridge rejects any command that was in flight
6
+ * when it happens, and a caller that reads that rejection as fatal cannot walk past the first link:
7
+ * reported against `reticle_verify action=crawl`, which died on link one of every Django, Rails and
8
+ * plain-HTML app.
9
+ *
10
+ * The prefix lives here so the producer and the predicate cannot drift apart. This repo has already
11
+ * shipped that exact bug twice — a remedy string restated in a second place, and a redaction name
12
+ * derived two different ways — and both times the halves disagreed silently.
13
+ */
14
+ /** The start of the reason the bridge hands to commands displaced by a reconnect. */
15
+ export declare const SESSION_REPLACED_PREFIX = "session replaced by a newer connection";
16
+ /** Build the full reason. The only place this sentence is written. */
17
+ export declare function sessionReplacedReason(id: string, url: string): string;
18
+ /**
19
+ * Was this failure a reconnect rather than a fault?
20
+ *
21
+ * Accepts the thrown value rather than a string so every call site can hand over whatever it caught
22
+ * without each one re-deriving a message — an `Error`, a bare string, or something else entirely.
23
+ */
24
+ export declare function isSessionReplacedError(error: unknown): boolean;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * "The page reconnected under the same id" — one sentence, and one way to recognise it.
3
+ *
4
+ * A server-rendered app reconnects its SDK on EVERY full page load, so on an MPA this is the normal
5
+ * consequence of following a link, not a fault. The bridge rejects any command that was in flight
6
+ * when it happens, and a caller that reads that rejection as fatal cannot walk past the first link:
7
+ * reported against `reticle_verify action=crawl`, which died on link one of every Django, Rails and
8
+ * plain-HTML app.
9
+ *
10
+ * The prefix lives here so the producer and the predicate cannot drift apart. This repo has already
11
+ * shipped that exact bug twice — a remedy string restated in a second place, and a redaction name
12
+ * derived two different ways — and both times the halves disagreed silently.
13
+ */
14
+ /** The start of the reason the bridge hands to commands displaced by a reconnect. */
15
+ export const SESSION_REPLACED_PREFIX = 'session replaced by a newer connection';
16
+ /** Build the full reason. The only place this sentence is written. */
17
+ export function sessionReplacedReason(id, url) {
18
+ return `${SESSION_REPLACED_PREFIX} claiming the same id (${id}) from ${url}`;
19
+ }
20
+ /**
21
+ * Was this failure a reconnect rather than a fault?
22
+ *
23
+ * Accepts the thrown value rather than a string so every call site can hand over whatever it caught
24
+ * without each one re-deriving a message — an `Error`, a bare string, or something else entirely.
25
+ */
26
+ export function isSessionReplacedError(error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ return message.includes(SESSION_REPLACED_PREFIX);
29
+ }
@@ -73,13 +73,29 @@ export function pickDocumentSuccessor(live, departed) {
73
73
  const origin = originOf(departed.url);
74
74
  if (origin === undefined)
75
75
  return undefined;
76
+ // A candidate must belong to the same app. `sessionStorage` is scoped to the ORIGIN rather than
77
+ // the app, so recycling a port (stop app A on :3000, start app B, reload the tab) has app B read
78
+ // app A's id out of storage and register under it with its own projectId. The guard was on the
79
+ // candidate search only, and a matching id returned before it ran — so an explicit sessionId was
80
+ // answered by a different product, and every observation afterwards was about the wrong app
81
+ // (#785).
82
+ const sameProject = (s) => departed.projectId === undefined || s.projectId === departed.projectId;
76
83
  const atOrigin = live.filter((s) => originOf(s.url) === origin);
77
84
  const sameId = atOrigin.find((s) => s.id === departed.id);
85
+ // `mayInherit` deliberately does NOT apply here. It asks whether ANOTHER session may take over a
86
+ // claimed identity, and a matching id is not another session — it is this one, reconnecting with
87
+ // the id that survived in sessionStorage. The claim lives in a URL PARAM, which an ordinary
88
+ // navigation drops (`navigate { url, reload: true }` reloads the bare address), so demanding it
89
+ // again would refuse the reload this whole mechanism exists for. A leased context has its own
90
+ // sessionStorage, so a human's tab cannot pick the id up that way — only by carrying the param,
91
+ // which satisfies `mayInherit` anyway. The gate would cost the ordinary case and buy nothing.
92
+ //
93
+ // Refusal, not a second guess, when the project does not match: an exact id match belonging to
94
+ // another app is the strongest evidence there is that the caller's id is stale, so falling
95
+ // through to whatever else sits on the origin would be the silent redirect this prevents.
78
96
  if (sameId !== undefined)
79
- return sameId;
80
- const others = atOrigin.filter((s) => s.id !== departed.id &&
81
- (departed.projectId === undefined || s.projectId === departed.projectId) &&
82
- mayInherit(departed, s));
97
+ return sameProject(sameId) ? sameId : undefined;
98
+ const others = atOrigin.filter((s) => s.id !== departed.id && sameProject(s) && mayInherit(departed, s));
83
99
  if (1 !== others.length)
84
100
  return undefined;
85
101
  return others[0];
@@ -43,6 +43,15 @@ export declare class Session {
43
43
  hasCapabilities: boolean;
44
44
  /** Set when the page's SDK version differs from the daemon's (see version-skew.ts). */
45
45
  versionSkew?: string;
46
+ /** SDK version from HELLO; kept so a remedy can check it applies — see body-capture-remedy.ts. */
47
+ sdkVersion?: string | undefined;
48
+ /** Whether the page records network bodies; undefined on an SDK too old to say. See HELLO. */
49
+ captureBodies?: boolean | undefined;
50
+ /**
51
+ * Whether this build stamps `data-reticle-source`, when the build plugin said. See HELLO.
52
+ * `false` separates "the project turned it off" from "nothing provides one"; undefined is unknown.
53
+ */
54
+ sourceMapping?: boolean | undefined;
46
55
  /**
47
56
  * Extra key names this app declared sensitive via `connect({ redact: { keys } })`. Held so the
48
57
  * DRIVEN path can redact them too — a request body the daemon captures from the network stack
@@ -86,6 +95,9 @@ export declare class Session {
86
95
  get currentDocumentId(): string | undefined;
87
96
  /** The edit epoch the most recent stamped event was observed under. See `#editEpoch`. */
88
97
  get currentEditEpoch(): number | undefined;
98
+ /** Seed/auth precondition failure reason, if any. */
99
+ setPreconditionFailure(reason: string): void;
100
+ preconditionFailure(): string | undefined;
89
101
  /** Re-stamp an incoming event with server-relative time, buffer it, and fan out. */
90
102
  pushEvent(event: ReticleEvent, byteSize?: number): void;
91
103
  recordActedRef(ref: string): void;
@@ -264,9 +276,5 @@ export declare class Session {
264
276
  /** Wired by SessionManager.add, the one place every session registers. */
265
277
  setViewers(read: () => Session[]): void;
266
278
  }
267
- /**
268
- * Re-exported from session-manager.ts so the public import path (`./session.js`) is unchanged for
269
- * the many call sites that resolve a target session. The class lives in its own file to keep both
270
- * units under the file-size cap.
271
- */
279
+ /** Re-exported so the public import path (`./session.js`) is unchanged. */
272
280
  export { SessionManager } from './session-manager.js';
@@ -3,7 +3,7 @@ import { LastAct } from './last-act.js';
3
3
  import { GapLedger } from '../honesty/gap-ledger.js';
4
4
  import { CaptureLedger } from '../honesty/feature-capture.js';
5
5
  import { commandTimeoutMessage } from './command-timeout.js';
6
- import { readHealthEvent } from './session-health.js';
6
+ import { readHealthEvent, pendingNavigationMs } from './session-health.js';
7
7
  import { MIRRORED_COMMANDS, mirroredNarration } from './session-mirror.js';
8
8
  /** The HUD does not need 200 frames to watch a counter climb; the last state always lands. */
9
9
  const IMPACT_PUSH_DEBOUNCE_MS = 700;
@@ -86,6 +86,15 @@ export class Session {
86
86
  hasCapabilities;
87
87
  /** Set when the page's SDK version differs from the daemon's (see version-skew.ts). */
88
88
  versionSkew;
89
+ /** SDK version from HELLO; kept so a remedy can check it applies — see body-capture-remedy.ts. */
90
+ sdkVersion;
91
+ /** Whether the page records network bodies; undefined on an SDK too old to say. See HELLO. */
92
+ captureBodies;
93
+ /**
94
+ * Whether this build stamps `data-reticle-source`, when the build plugin said. See HELLO.
95
+ * `false` separates "the project turned it off" from "nothing provides one"; undefined is unknown.
96
+ */
97
+ sourceMapping;
89
98
  /**
90
99
  * Extra key names this app declared sensitive via `connect({ redact: { keys } })`. Held so the
91
100
  * DRIVEN path can redact them too — a request body the daemon captures from the network stack
@@ -128,28 +137,16 @@ export class Session {
128
137
  #journalReader;
129
138
  /** What this session has learned by watching its own stream: ambient churn + blind-spot levels. */
130
139
  #observed = new ObservedState();
140
+ /** Set when a required application precondition (e.g. seedStorage) was not established. */
141
+ #preconditionFailure;
131
142
  /**
132
143
  * Which document is on screen right now — the one the most recent stamped event was observed under.
133
- *
134
- * DERIVED, never announced. The SDK mints a document id once per real document and stamps it on
135
- * every event, so the stream already carries the answer; asking the page for it separately would be
136
- * a second source of truth that can disagree with the evidence it is supposed to scope. A full
137
- * navigation or a reload builds a new document, mints a new id, and the first event carrying it
138
- * moves this forward — which is exactly the moment the previous document's evidence stops being
139
- * about the world. An SPA route change keeps the same document and so keeps the same id, which is
140
- * correct rather than a limitation: same JavaScript context, same in-flight requests, same evidence.
141
- *
142
- * An UNSTAMPED event never clears this. An SDK older than the field stamps nothing, and letting one
143
- * such event blank the current document would make every later comparison vacuous.
144
+ * DERIVED, never announced: the SDK stamps it on events. An unstamped event never clears this.
144
145
  */
145
146
  #documentId;
146
147
  /**
147
- * The edit epoch of the most recent stamped event. DERIVED for the same reason `#documentId` is:
148
- * the stream already carries it, and asking the page separately would be a second source of truth.
149
- *
150
- * A hot update advances it INSIDE the same document, which is the case `#documentId` structurally
151
- * cannot see — no navigation, same page, replaced code. An unstamped event never clears it: most
152
- * pages have no hot-update channel at all, so absence is "unknown", never "back to no edits".
148
+ * The edit epoch of the most recent stamped event. DERIVED for the same reason `#documentId` is.
149
+ * A hot update advances it inside the same document. An unstamped event never clears it.
153
150
  */
154
151
  #editEpoch;
155
152
  constructor(hello, socket, clock) {
@@ -159,6 +156,7 @@ export class Session {
159
156
  this.title = hello.title;
160
157
  this.adapters = hello.adapters;
161
158
  this.hasCapabilities = hello.hasCapabilities ?? false;
159
+ this.sourceMapping = hello.sourceMapping;
162
160
  this.redactKeys = hello.redactKeys ?? [];
163
161
  this.#socket = socket;
164
162
  this.#clock = clock;
@@ -214,10 +212,17 @@ export class Session {
214
212
  }
215
213
  /** The attachable health block — single source of truth for the tools. */
216
214
  health() {
215
+ // From event t=0, not a cursor: a wedge that began before the current action is exactly the
216
+ // case a per-window reading cannot see, and is the one both reporters hit.
217
+ const stuck = pendingNavigationMs(this.eventsSince(0), this.elapsed());
217
218
  const base = {
218
219
  lastSeenMs: this.lastSeenMs(),
219
220
  throttled: this.throttled(),
220
221
  focused: this.#focused,
222
+ ...(stuck === undefined ? {} : { pendingNavigationMs: stuck }),
223
+ // Carried onto every act/assert result, not just reticle_sessions: skew drops actions
224
+ // SILENTLY, and the fields it contradicts are on the act verdict.
225
+ ...(this.versionSkew === undefined ? {} : { versionSkew: this.versionSkew }),
221
226
  };
222
227
  // attach the escape-hatch hint only when un-scriptable (keeps field absent otherwise).
223
228
  const recommendation = buildSessionRecommendation({
@@ -268,6 +273,13 @@ export class Session {
268
273
  get currentEditEpoch() {
269
274
  return this.#editEpoch;
270
275
  }
276
+ /** Seed/auth precondition failure reason, if any. */
277
+ setPreconditionFailure(reason) {
278
+ this.#preconditionFailure = reason;
279
+ }
280
+ preconditionFailure() {
281
+ return this.#preconditionFailure;
282
+ }
271
283
  /** Re-stamp an incoming event with server-relative time, buffer it, and fan out. */
272
284
  pushEvent(event, byteSize) {
273
285
  if (event.documentId !== undefined)
@@ -875,9 +887,5 @@ export class Session {
875
887
  }
876
888
  }
877
889
  }
878
- /**
879
- * Re-exported from session-manager.ts so the public import path (`./session.js`) is unchanged for
880
- * the many call sites that resolve a target session. The class lives in its own file to keep both
881
- * units under the file-size cap.
882
- */
890
+ /** Re-exported so the public import path (`./session.js`) is unchanged. */
883
891
  export { SessionManager } from './session-manager.js';
@@ -40,7 +40,7 @@ export interface PlatformPaths {
40
40
  readonly linux: string;
41
41
  readonly win32: string;
42
42
  }
43
- export interface AgentClient {
43
+ interface AgentClient {
44
44
  readonly id: string;
45
45
  readonly name: string;
46
46
  readonly confidence: AgentConfidence;
@@ -68,7 +68,7 @@ export interface AgentPlanStep {
68
68
  readonly entry?: Readonly<Record<string, unknown>> | undefined;
69
69
  readonly why: string;
70
70
  }
71
- export interface AgentPlanInput {
71
+ interface AgentPlanInput {
72
72
  readonly home: string;
73
73
  readonly platform: keyof PlatformPaths;
74
74
  readonly exists: (path: string) => boolean;
@@ -94,3 +94,4 @@ export declare const joinFor: (os: keyof PlatformPaths) => ((...parts: string[])
94
94
  * makes the Windows rows testable from any machine.
95
95
  */
96
96
  export declare function planAgentConfigs(input: AgentPlanInput): AgentPlanStep[];
97
+ export {};
@@ -17,7 +17,7 @@ export interface AgentWriterIo {
17
17
  readonly writeFile: (path: string, contents: string) => void;
18
18
  readonly mkdirp: (dir: string) => void;
19
19
  }
20
- export interface AgentWriteResult {
20
+ interface AgentWriteResult {
21
21
  readonly id: string;
22
22
  readonly name: string;
23
23
  readonly file: string;
@@ -35,7 +35,7 @@ export interface AgentWriteResult {
35
35
  export declare function applyAgentPlan(plan: readonly AgentPlanStep[], io: AgentWriterIo): AgentWriteResult[];
36
36
  /** The `/reticle` instruction, for agents that load skills from a directory. */
37
37
  export declare const RETICLE_SKILL = "---\nname: reticle\ndescription: Verify a change against the running app, from inside it, before calling it done.\n---\n\nReticle exposes the running app to you as `reticle_*` MCP tools. Look, act, observe, assert.\n\nOnly `reticle_act_and_wait` and `reticle_assert` produce a verdict. Everything else moves or\nreads the app and proves nothing, so a drive that ends without one of those has no result however\nmany tools it used.\n\n`verified: \"unknown\"` is not a pass: it means Reticle could not tell what happened.\n`verified: \"no-fault\"` is not a pass either, because nothing was declared to prove. Never weaken a\ncheck to make it pass.\n\nThe cheapest path that answers the question:\n\n- \"did my edit break anything?\" -> `reticle_run({ tool: \"reticle_verify\", args: { action: \"change\", files: [...] } })`\n- \"does this known journey still work?\" -> `reticle_run({ tool: \"reticle_flow_replay\", args: { flowName: \"...\" } })`\n- \"does this new behaviour work?\" -> `reticle_act_sequence` for the setup, then ONE `reticle_act_and_wait`\n";
38
- export interface SkillWriteResult {
38
+ interface SkillWriteResult {
39
39
  readonly id: string;
40
40
  readonly file: string;
41
41
  }
@@ -49,3 +49,4 @@ export declare function applyAgentSkills(io: AgentWriterIo, where: {
49
49
  readonly home: string;
50
50
  readonly platform: keyof PlatformPaths;
51
51
  }): SkillWriteResult[];
52
+ export {};
@@ -33,7 +33,7 @@ export interface MigrationDeps {
33
33
  readonly version: string;
34
34
  readonly log: (event: string, data: Record<string, unknown>) => void;
35
35
  }
36
- export interface MigrationResult {
36
+ interface MigrationResult {
37
37
  readonly ran: boolean;
38
38
  readonly granted: readonly string[];
39
39
  readonly deferred: readonly string[];
@@ -45,3 +45,4 @@ export interface MigrationResult {
45
45
  * re-scan on every editor launch for the rest of the version's life.
46
46
  */
47
47
  export declare function migrateApprovals(deps: MigrationDeps): MigrationResult;
48
+ export {};
@@ -47,7 +47,7 @@ export function migrateApprovals(deps) {
47
47
  }
48
48
  let results = [];
49
49
  try {
50
- results = grantAutoApproval(io, { home, platform }, undefined, { onlyIfNoSupersede: true });
50
+ results = grantAutoApproval(io, { home, platform });
51
51
  }
52
52
  catch {
53
53
  return NOTHING;
@@ -13,9 +13,12 @@
13
13
  * defensible; the same edit made globally would hand the same pass to every other MCP server
14
14
  * they ever install, including ones that reach the network. Their approval gate is not ours to
15
15
  * dismantle, only ours to step out of.
16
- * 2. Nothing is written for an agent that is not installed. An absent agent has no Accept button to
17
- * remove, and the file would be litter — Cursor's especially, since that one TAKES OVER from an
18
- * allowlist we cannot read.
16
+ * 2. Nothing is CREATED where the file itself takes over settings we cannot read — Cursor's
17
+ * permissions.json supersedes its in-app allowlist and pins the run mode to Allowlist, so a user
18
+ * who ran everything without asking loses that and cannot switch back while the file exists.
19
+ * Merging into one they already have is fine; making one is not, so that case is DEFERRED with
20
+ * the in-app step to take instead. Nothing is written at all for an agent that is not installed:
21
+ * an absent agent has no Accept button to remove.
19
22
  *
20
23
  * Codex is absent by design: its approval policy is global (`approval_policy`), so there is no
21
24
  * reticle-shaped rule to write, and its config is TOML, which this repo never rewrites. Its
@@ -36,8 +39,8 @@ export interface ApprovalGrant {
36
39
  /** Adds our rule to a parsed config, leaving everything else exactly as it was. */
37
40
  readonly grant: (current: Record<string, unknown>) => Record<string, unknown>;
38
41
  /**
39
- * True where creating this file supersedes an in-app allowlist we cannot read, so the user has to
40
- * be told rather than have their other servers quietly start prompting again.
42
+ * True where CREATING this file supersedes in-app settings we can neither read nor restore. Such
43
+ * a grant is only ever merged into a file the user already owns, never created.
41
44
  */
42
45
  readonly supersedesInApp?: boolean;
43
46
  }
@@ -60,26 +63,15 @@ export interface ApprovalResult {
60
63
  /** Set where the user has to be told something the write alone does not say. */
61
64
  readonly warn?: string;
62
65
  }
63
- export interface ApprovalWhere {
66
+ interface ApprovalWhere {
64
67
  readonly home: string;
65
68
  readonly platform: keyof PlatformPaths;
66
69
  }
67
- export interface ApprovalOptions {
68
- /**
69
- * Refuse any grant that would CREATE a file superseding an allowlist we cannot read.
70
- *
71
- * Set on the unattended path. Writing Cursor's permissions.json for the first time takes over
72
- * from what the user approved inside the app, which is a fair trade when they just ran a command
73
- * and can read the line saying so, and not a fair trade at all when a version bump did it behind
74
- * them: their OTHER MCP servers would start prompting again and nothing would connect that to us.
75
- * Merging into a file they already own stays safe either way.
76
- */
77
- readonly onlyIfNoSupersede?: boolean;
78
- }
79
70
  /**
80
71
  * Pre-approve Reticle's tools everywhere the machine has an agent that would otherwise ask.
81
72
  *
82
73
  * A client that cannot be written is reported, never thrown: one unwritable settings file is not a
83
74
  * reason to leave the other three prompting.
84
75
  */
85
- export declare function grantAutoApproval(io: AgentWriterIo, where: ApprovalWhere, grants?: readonly ApprovalGrant[], options?: ApprovalOptions): ApprovalResult[];
76
+ export declare function grantAutoApproval(io: AgentWriterIo, where: ApprovalWhere, grants?: readonly ApprovalGrant[]): ApprovalResult[];
77
+ export {};
@@ -13,9 +13,12 @@
13
13
  * defensible; the same edit made globally would hand the same pass to every other MCP server
14
14
  * they ever install, including ones that reach the network. Their approval gate is not ours to
15
15
  * dismantle, only ours to step out of.
16
- * 2. Nothing is written for an agent that is not installed. An absent agent has no Accept button to
17
- * remove, and the file would be litter — Cursor's especially, since that one TAKES OVER from an
18
- * allowlist we cannot read.
16
+ * 2. Nothing is CREATED where the file itself takes over settings we cannot read — Cursor's
17
+ * permissions.json supersedes its in-app allowlist and pins the run mode to Allowlist, so a user
18
+ * who ran everything without asking loses that and cannot switch back while the file exists.
19
+ * Merging into one they already have is fine; making one is not, so that case is DEFERRED with
20
+ * the in-app step to take instead. Nothing is written at all for an agent that is not installed:
21
+ * an absent agent has no Accept button to remove.
19
22
  *
20
23
  * Codex is absent by design: its approval policy is global (`approval_policy`), so there is no
21
24
  * reticle-shaped rule to write, and its config is TOML, which this repo never rewrites. Its
@@ -54,8 +57,11 @@ export const APPROVAL_GRANTS = [
54
57
  markers: [home('.cursor')],
55
58
  rule: 'mcpAllowlist += "reticle:*"',
56
59
  // Documented: "when a key appears in permissions.json, it fully replaces the in-app allowlist
57
- // for that type". Merging keeps everything already in the FILE; it cannot restore what was only
58
- // ever clicked in the UI, so the user is told.
60
+ // for that type" — and in practice it costs more than that. A non-empty mcpAllowlist also locks
61
+ // the run mode to Allowlist, so a user who had Run Everything ("YOLO") on loses it and cannot
62
+ // turn it back on while the file exists; Cursor has acknowledged that as their bug. Merging into
63
+ // a file the user already made is safe — they are already in Allowlist mode. Creating one is
64
+ // not, so we never do: see the DEFERRED branch below.
59
65
  supersedesInApp: true,
60
66
  grant: (c) => addToList(c, null, 'mcpAllowlist', `${RETICLE_KEY}:*`),
61
67
  },
@@ -107,7 +113,7 @@ export const ApprovalOutcome = {
107
113
  * A client that cannot be written is reported, never thrown: one unwritable settings file is not a
108
114
  * reason to leave the other three prompting.
109
115
  */
110
- export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS, options = {}) {
116
+ export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS) {
111
117
  const join = joinFor(where.platform);
112
118
  return grants.map((grant) => {
113
119
  const file = join(where.home, grant.paths[where.platform]);
@@ -117,11 +123,11 @@ export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS, options =
117
123
  return { ...base, outcome: ApprovalOutcome.ABSENT };
118
124
  try {
119
125
  const existed = io.exists(file);
120
- if (true === options.onlyIfNoSupersede && true === grant.supersedesInApp && !existed) {
126
+ if (true === grant.supersedesInApp && !existed) {
121
127
  return {
122
128
  ...base,
123
129
  outcome: ApprovalOutcome.DEFERRED,
124
- warn: `creating ${file} would supersede what you approved inside ${grant.name}, which is not something to do unannounced. Run: npx @reticlehq/server init --files-only`,
130
+ warn: `left ${grant.name}'s approvals alone — creating ${file} would take over its in-app settings and pin the run mode to Allowlist. If ${grant.name} asks before every reticle tool call, add \`${RETICLE_KEY}:*\` under Settings → Agents → Approvals & Execution (nothing to do if you run everything already).`,
125
131
  };
126
132
  }
127
133
  // A settings file we cannot parse is left exactly as it is. Reformatting somebody's config to
@@ -134,10 +140,7 @@ export function grantAutoApproval(io, where, grants = APPROVAL_GRANTS, options =
134
140
  return { ...base, outcome: ApprovalOutcome.ALREADY };
135
141
  io.mkdirp(file.slice(0, Math.max(0, file.lastIndexOf('/'))));
136
142
  io.writeFile(file, `${JSON.stringify(next, null, INDENT)}\n`);
137
- const warn = true === grant.supersedesInApp && !existed
138
- ? `${file} now governs which MCP tools run without asking; anything you had approved inside ${grant.name} itself will ask again until it is listed here too`
139
- : undefined;
140
- return { ...base, outcome: ApprovalOutcome.GRANTED, ...(undefined === warn ? {} : { warn }) };
143
+ return { ...base, outcome: ApprovalOutcome.GRANTED };
141
144
  }
142
145
  catch (err) {
143
146
  return {
@@ -31,7 +31,7 @@
31
31
  import { InitConfirmation, RETICLE_DEFAULT_PORT } from '@reticlehq/core';
32
32
  import { fetchStatus, summarizeStatus } from '../cli/cli-launch.js';
33
33
  import { readDevServers } from '../daemon/dev-servers.js';
34
- import { devServersForProject } from '@reticlehq/core';
34
+ import { devServersForProject } from '@reticlehq/core/artifacts';
35
35
  import { readProjectId } from '../cli/cli-port.js';
36
36
  import { reticleStateHome } from '../daemon/daemon.js';
37
37
  import { reportInitOutcome } from '../telemetry/init-telemetry.js';
@@ -24,7 +24,7 @@ export declare const AppShape: {
24
24
  export type AppShape = (typeof AppShape)[keyof typeof AppShape];
25
25
  export declare const isDesktop: (shape: AppShape) => boolean;
26
26
  /** Files and dependencies that name the shell, read through an injected reader. */
27
- export interface ShapeEvidence {
27
+ interface ShapeEvidence {
28
28
  /** `src-tauri/tauri.conf.json` exists. */
29
29
  readonly hasTauriConf: boolean;
30
30
  /** `electron` is a dependency or devDependency. */
@@ -44,7 +44,7 @@ export declare function isDesktopOrigin(url: string): boolean;
44
44
  * Returned as data rather than branched at each call site, so the differences are visible in one
45
45
  * place and testable without a desktop runtime.
46
46
  */
47
- export interface ShapePolicy {
47
+ interface ShapePolicy {
48
48
  /** Open a browser at the url. False for desktop: the app window is already the client. */
49
49
  readonly openBrowser: boolean;
50
50
  /** Require an HTTP response before looking for a session. */
@@ -55,3 +55,4 @@ export interface ShapePolicy {
55
55
  readonly note: string | undefined;
56
56
  }
57
57
  export declare function policyFor(shape: AppShape): ShapePolicy;
58
+ export {};