@reticlehq/server 2.11.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (513) hide show
  1. package/SKILL.md +93 -111
  2. package/dist/bridge/auth-failure-reason.d.ts +6 -2
  3. package/dist/bridge/auth-failure-reason.js +15 -8
  4. package/dist/bridge/bridge.d.ts +18 -14
  5. package/dist/bridge/bridge.js +248 -165
  6. package/dist/bridge/bridge.test-harness.d.ts +2 -0
  7. package/dist/bridge/bridge.test-harness.js +8 -1
  8. package/dist/bridge/pairing-token.d.ts +5 -0
  9. package/dist/bridge/pairing-token.js +28 -0
  10. package/dist/bridge/protocol-skew.d.ts +37 -0
  11. package/dist/bridge/protocol-skew.js +45 -0
  12. package/dist/capsule/capsule-store.d.ts +8 -7
  13. package/dist/capsule/capsule-store.js +1 -1
  14. package/dist/capsule/causal-summary.d.ts +2 -2
  15. package/dist/capsule/causal-summary.js +7 -2
  16. package/dist/chromium-launch-options.d.ts +28 -0
  17. package/dist/chromium-launch-options.js +29 -0
  18. package/dist/cli/cli-doctor.js +39 -1
  19. package/dist/cli/cli-flow-commands.d.ts +3 -1
  20. package/dist/cli/cli-flow-commands.js +34 -4
  21. package/dist/cli/cli-kill.d.ts +2 -1
  22. package/dist/cli/cli-launch.d.ts +11 -1
  23. package/dist/cli/cli-launch.js +59 -10
  24. package/dist/cli/cli-parse.d.ts +17 -1
  25. package/dist/cli/cli-parse.js +238 -12
  26. package/dist/cli/cli-update-commands.js +20 -1
  27. package/dist/cli/cli-verify.d.ts +2 -1
  28. package/dist/cli/cli-verify.js +5 -4
  29. package/dist/cli/cloud-cli.js +428 -253
  30. package/dist/cli/cloud-keystore.d.ts +37 -0
  31. package/dist/cli/cloud-keystore.js +97 -0
  32. package/dist/cli/cloud-kit.d.ts +37 -0
  33. package/dist/cli/cloud-kit.js +121 -0
  34. package/dist/cli/cloud-login.d.ts +17 -0
  35. package/dist/cli/cloud-login.js +183 -0
  36. package/dist/cli/cloud-session.d.ts +48 -0
  37. package/dist/cli/cloud-session.js +53 -0
  38. package/dist/cli/config-discovery.d.ts +2 -1
  39. package/dist/cli/config-discovery.js +1 -0
  40. package/dist/cli/daemon-start-options.d.ts +32 -0
  41. package/dist/cli/daemon-start-options.js +38 -0
  42. package/dist/cli/device-flow.d.ts +76 -0
  43. package/dist/cli/device-flow.js +56 -0
  44. package/dist/cli/doctor-rows.d.ts +12 -1
  45. package/dist/cli/doctor-rows.js +12 -1
  46. package/dist/cli/drive-attach.d.ts +13 -0
  47. package/dist/cli/drive-attach.js +18 -0
  48. package/dist/cli/drive-command.js +7 -3
  49. package/dist/cli/gate-exit.d.ts +21 -0
  50. package/dist/cli/gate-exit.js +21 -0
  51. package/dist/cli/gate-hook-message.d.ts +33 -0
  52. package/dist/cli/gate-hook-message.js +46 -0
  53. package/dist/cli/mcp-command.d.ts +25 -0
  54. package/dist/cli/mcp-command.js +174 -0
  55. package/dist/cli/project-name.d.ts +30 -0
  56. package/dist/cli/project-name.js +44 -0
  57. package/dist/cli/sibling-ports.d.ts +37 -0
  58. package/dist/cli/sibling-ports.js +56 -0
  59. package/dist/cli/status-next-action.d.ts +14 -0
  60. package/dist/cli/status-next-action.js +4 -4
  61. package/dist/cli.d.ts +1 -0
  62. package/dist/cli.js +167 -186
  63. package/dist/cloud/cloud-config.d.ts +24 -2
  64. package/dist/cloud/cloud-config.js +64 -5
  65. package/dist/cloud/cloud-sync.d.ts +16 -5
  66. package/dist/cloud/cloud-sync.js +64 -1
  67. package/dist/cloud/sync-cycle.d.ts +110 -0
  68. package/dist/cloud/sync-cycle.js +270 -0
  69. package/dist/cloud/sync-daemon.d.ts +69 -0
  70. package/dist/cloud/sync-daemon.js +260 -0
  71. package/dist/cloud/sync-disk.d.ts +9 -0
  72. package/dist/cloud/sync-disk.js +191 -0
  73. package/dist/cloud/sync-hash.d.ts +2 -0
  74. package/dist/cloud/sync-hash.js +35 -0
  75. package/dist/crawl/crawl-tools.js +5 -3
  76. package/dist/crawl/crawl.d.ts +12 -13
  77. package/dist/crawl/crawl.js +25 -3
  78. package/dist/daemon/daemon-resolve.d.ts +122 -0
  79. package/dist/daemon/daemon-resolve.js +187 -0
  80. package/dist/daemon/dev-servers.d.ts +15 -0
  81. package/dist/daemon/dev-servers.js +68 -0
  82. package/dist/daemon/free-port.d.ts +29 -0
  83. package/dist/daemon/free-port.js +55 -0
  84. package/dist/daemon/heartbeat.d.ts +0 -4
  85. package/dist/daemon/heartbeat.js +2 -2
  86. package/dist/daemon/wake-decision.d.ts +32 -0
  87. package/dist/daemon/wake-decision.js +38 -0
  88. package/dist/dev/stale-issue-guard.d.ts +0 -2
  89. package/dist/dev/stale-issue-guard.js +1 -1
  90. package/dist/domain/domain-tools.js +2 -2
  91. package/dist/events/contradictions.d.ts +59 -0
  92. package/dist/events/contradictions.js +253 -27
  93. package/dist/events/declared.d.ts +17 -0
  94. package/dist/events/declared.js +43 -1
  95. package/dist/events/echo-mismatch.js +65 -1
  96. package/dist/events/event-filters.d.ts +8 -0
  97. package/dist/events/event-filters.js +19 -2
  98. package/dist/events/observed-in-window.d.ts +12 -0
  99. package/dist/events/observed-in-window.js +17 -0
  100. package/dist/events/predicate-asks.d.ts +13 -0
  101. package/dist/events/predicate-asks.js +22 -0
  102. package/dist/events/predicate-eval.js +117 -27
  103. package/dist/events/predicate-parse.d.ts +0 -6
  104. package/dist/events/predicate-parse.js +43 -3
  105. package/dist/events/predicate-precheck.d.ts +8 -0
  106. package/dist/events/predicate-precheck.js +41 -0
  107. package/dist/events/predicate-route.d.ts +14 -0
  108. package/dist/events/predicate-route.js +36 -8
  109. package/dist/events/predicate-schema.d.ts +44 -0
  110. package/dist/events/predicate-schema.js +126 -23
  111. package/dist/events/predicate.d.ts +20 -0
  112. package/dist/events/predicate.js +77 -6
  113. package/dist/events/split-text-miss.d.ts +30 -0
  114. package/dist/events/split-text-miss.js +44 -0
  115. package/dist/flows/coverage.d.ts +0 -8
  116. package/dist/flows/coverage.js +1 -1
  117. package/dist/flows/decision.d.ts +5 -1
  118. package/dist/flows/decision.js +33 -7
  119. package/dist/flows/flake.d.ts +6 -8
  120. package/dist/flows/flake.js +1 -1
  121. package/dist/flows/flow-intent.d.ts +52 -0
  122. package/dist/flows/flow-intent.js +123 -0
  123. package/dist/flows/flow-memory-consult.d.ts +55 -0
  124. package/dist/flows/flow-memory-consult.js +38 -0
  125. package/dist/flows/flow-replay-run.d.ts +18 -7
  126. package/dist/flows/flow-replay-run.js +263 -23
  127. package/dist/flows/flow-replay.js +11 -2
  128. package/dist/flows/flow-step-runners.d.ts +8 -0
  129. package/dist/flows/flow-step-runners.js +8 -2
  130. package/dist/flows/flow-store-for-session.d.ts +31 -0
  131. package/dist/flows/flow-store-for-session.js +33 -0
  132. package/dist/flows/flow-success.js +15 -3
  133. package/dist/flows/flow-tools.js +87 -33
  134. package/dist/flows/flows.d.ts +14 -1
  135. package/dist/flows/flows.js +502 -24
  136. package/dist/flows/heal-run.js +5 -2
  137. package/dist/flows/predicate-to-expect.d.ts +17 -12
  138. package/dist/flows/predicate-to-expect.js +36 -15
  139. package/dist/flows/recordings.d.ts +13 -1
  140. package/dist/flows/recordings.js +17 -2
  141. package/dist/flows/replay.d.ts +22 -2
  142. package/dist/flows/replay.js +28 -2
  143. package/dist/flows/server-verify.js +16 -2
  144. package/dist/flows/verify-change-tools.js +9 -3
  145. package/dist/honesty/blind-spots.d.ts +51 -15
  146. package/dist/honesty/blind-spots.js +64 -4
  147. package/dist/honesty/feature-capture.d.ts +150 -0
  148. package/dist/honesty/feature-capture.js +233 -0
  149. package/dist/honesty/honesty.d.ts +24 -0
  150. package/dist/honesty/honesty.js +25 -1
  151. package/dist/honesty/instrumentation-gaps.d.ts +39 -6
  152. package/dist/honesty/instrumentation-gaps.js +88 -5
  153. package/dist/honesty/tool-hit-rate.d.ts +86 -0
  154. package/dist/honesty/tool-hit-rate.js +90 -0
  155. package/dist/honesty/undeclared-change.d.ts +51 -0
  156. package/dist/honesty/undeclared-change.js +56 -0
  157. package/dist/honesty/verified.d.ts +16 -0
  158. package/dist/honesty/verified.js +84 -5
  159. package/dist/http-server.d.ts +0 -1
  160. package/dist/http-server.js +13 -1
  161. package/dist/hunt/hunt-report.d.ts +0 -2
  162. package/dist/hunt/hunt-report.js +1 -1
  163. package/dist/impact/impact-recorder.d.ts +31 -8
  164. package/dist/impact/impact-recorder.js +104 -21
  165. package/dist/impact/impact-store.d.ts +13 -4
  166. package/dist/impact/impact-store.js +44 -3
  167. package/dist/index.d.ts +4 -1
  168. package/dist/index.js +113 -7
  169. package/dist/init/agent-rules.d.ts +30 -6
  170. package/dist/init/agent-rules.js +54 -13
  171. package/dist/init/astro-patch.d.ts +16 -0
  172. package/dist/init/astro-patch.js +28 -0
  173. package/dist/init/capabilities.d.ts +4 -3
  174. package/dist/init/capabilities.js +23 -3
  175. package/dist/init/closing-hint.d.ts +36 -0
  176. package/dist/init/closing-hint.js +120 -0
  177. package/dist/init/confirm.d.ts +27 -28
  178. package/dist/init/confirm.js +51 -11
  179. package/dist/init/cra.d.ts +34 -5
  180. package/dist/init/cra.js +96 -25
  181. package/dist/init/csp-check.js +17 -2
  182. package/dist/init/csp-doctor.d.ts +8 -1
  183. package/dist/init/csp-doctor.js +8 -3
  184. package/dist/init/dev-script.d.ts +50 -0
  185. package/dist/init/dev-script.js +66 -0
  186. package/dist/init/format-generated.d.ts +18 -0
  187. package/dist/init/format-generated.js +39 -0
  188. package/dist/init/install-hint.d.ts +9 -0
  189. package/dist/init/install-hint.js +29 -0
  190. package/dist/init/mcp-clients.d.ts +7 -0
  191. package/dist/init/mcp-clients.js +7 -0
  192. package/dist/init/mcp.d.ts +17 -6
  193. package/dist/init/mcp.js +27 -11
  194. package/dist/init/node-io.js +11 -1
  195. package/dist/init/plan-framework.d.ts +0 -5
  196. package/dist/init/plan-framework.js +147 -37
  197. package/dist/init/plan.d.ts +30 -37
  198. package/dist/init/plan.js +50 -60
  199. package/dist/init/preflight.d.ts +28 -0
  200. package/dist/init/preflight.js +38 -0
  201. package/dist/init/refresh-rules.d.ts +39 -0
  202. package/dist/init/refresh-rules.js +51 -0
  203. package/dist/init/run.d.ts +32 -35
  204. package/dist/init/run.js +151 -202
  205. package/dist/init/slash-command.d.ts +1 -1
  206. package/dist/init/snippets.js +90 -31
  207. package/dist/init/vite-config.d.ts +1 -1
  208. package/dist/init/vite-config.js +39 -25
  209. package/dist/init/workspace-apps.d.ts +17 -10
  210. package/dist/init/workspace-apps.js +110 -0
  211. package/dist/input/network-mock-tools.js +3 -6
  212. package/dist/input/real-input.d.ts +2 -0
  213. package/dist/input/real-input.js +28 -7
  214. package/dist/input/scroll-find.d.ts +7 -4
  215. package/dist/input/scroll-find.js +63 -25
  216. package/dist/input/scroll-tools.js +4 -6
  217. package/dist/input/viewport-tools.js +5 -4
  218. package/dist/intent/inline-intent.d.ts +37 -0
  219. package/dist/intent/inline-intent.js +193 -0
  220. package/dist/intent/intent-shard-store.d.ts +72 -0
  221. package/dist/intent/intent-shard-store.js +203 -0
  222. package/dist/intent/intent-shard.d.ts +411 -0
  223. package/dist/intent/intent-shard.js +176 -0
  224. package/dist/intent/intent-store.d.ts +49 -0
  225. package/dist/intent/intent-store.js +133 -0
  226. package/dist/intent/intent-subject.d.ts +45 -0
  227. package/dist/intent/intent-subject.js +177 -0
  228. package/dist/intent/intent-tools.d.ts +2 -0
  229. package/dist/intent/intent-tools.js +143 -0
  230. package/dist/intent/open-intents.d.ts +42 -0
  231. package/dist/intent/open-intents.js +40 -0
  232. package/dist/journal/envelope.d.ts +26 -26
  233. package/dist/journal/journal-recorder.d.ts +23 -0
  234. package/dist/journal/journal-recorder.js +35 -0
  235. package/dist/license/license-env.d.ts +29 -0
  236. package/dist/license/license-env.js +148 -0
  237. package/dist/loopback-agent.d.ts +31 -0
  238. package/dist/loopback-agent.js +57 -0
  239. package/dist/mcp/attach-memory.d.ts +83 -0
  240. package/dist/mcp/attach-memory.js +185 -0
  241. package/dist/mcp/client-identity.d.ts +31 -0
  242. package/dist/mcp/client-identity.js +51 -0
  243. package/dist/mcp/mcp-post-transport.d.ts +26 -0
  244. package/dist/mcp/mcp-post-transport.js +109 -0
  245. package/dist/mcp/mcp-proxy.d.ts +2 -8
  246. package/dist/mcp/mcp-proxy.js +64 -62
  247. package/dist/mcp/mcp.d.ts +2 -0
  248. package/dist/mcp/mcp.js +52 -24
  249. package/dist/mcp/proxy-backoff.d.ts +11 -0
  250. package/dist/mcp/proxy-backoff.js +13 -0
  251. package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
  252. package/dist/mcp/proxy-daemon-probe.js +7 -1
  253. package/dist/mcp/proxy-handshake.d.ts +23 -0
  254. package/dist/mcp/proxy-handshake.js +31 -0
  255. package/dist/mcp/server-instructions.js +45 -3
  256. package/dist/mcp/tool-catalog-cache.d.ts +16 -0
  257. package/dist/mcp/tool-catalog-cache.js +22 -0
  258. package/dist/memory/memory-tools.d.ts +2 -0
  259. package/dist/memory/memory-tools.js +82 -0
  260. package/dist/memory/project-memory.d.ts +34 -0
  261. package/dist/memory/project-memory.js +96 -0
  262. package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
  263. package/dist/oracles/flow-instrument-gaps.js +4 -5
  264. package/dist/oracles/propose-consequences.js +12 -3
  265. package/dist/oracles/self-instrument.d.ts +9 -9
  266. package/dist/oracles/self-instrument.js +19 -13
  267. package/dist/pool/browser-pool.d.ts +75 -5
  268. package/dist/pool/browser-pool.js +144 -5
  269. package/dist/pool/playwright-launcher.js +12 -1
  270. package/dist/project/fs-port.d.ts +3 -0
  271. package/dist/project/fs-port.js +3 -2
  272. package/dist/project/memory-fs.js +6 -1
  273. package/dist/project/project-tools.js +6 -35
  274. package/dist/project/reticle-dir.d.ts +17 -2
  275. package/dist/project/reticle-dir.js +22 -4
  276. package/dist/runs/context-tools.d.ts +2 -0
  277. package/dist/runs/context-tools.js +89 -0
  278. package/dist/runs/run-context.d.ts +40 -0
  279. package/dist/runs/run-context.js +158 -0
  280. package/dist/runs/run-diff.d.ts +2 -1
  281. package/dist/runs/run-store.d.ts +10 -0
  282. package/dist/runs/run-store.js +10 -0
  283. package/dist/runs/verification-sync.d.ts +0 -11
  284. package/dist/runs/verification-sync.js +24 -3
  285. package/dist/runs/verify-server.d.ts +8 -1
  286. package/dist/runs/verify-server.js +14 -2
  287. package/dist/session/command-timeout.d.ts +5 -2
  288. package/dist/session/command-timeout.js +5 -4
  289. package/dist/session/dev-server-probe.d.ts +0 -8
  290. package/dist/session/dev-server-probe.js +1 -1
  291. package/dist/session/last-act.d.ts +7 -1
  292. package/dist/session/last-act.js +2 -2
  293. package/dist/session/lease-visibility.d.ts +67 -0
  294. package/dist/session/lease-visibility.js +76 -0
  295. package/dist/session/live-control-tools.js +1 -42
  296. package/dist/session/no-session-diagnosis.d.ts +33 -0
  297. package/dist/session/no-session-diagnosis.js +82 -24
  298. package/dist/session/no-session-next-action.d.ts +12 -0
  299. package/dist/session/no-session-next-action.js +77 -6
  300. package/dist/session/no-session-watch.d.ts +18 -6
  301. package/dist/session/no-session-watch.js +125 -75
  302. package/dist/session/output-budget.js +4 -1
  303. package/dist/session/pending-commands.d.ts +11 -1
  304. package/dist/session/pending-commands.js +12 -2
  305. package/dist/session/resolve-within.d.ts +34 -0
  306. package/dist/session/resolve-within.js +53 -0
  307. package/dist/session/session-health.d.ts +9 -0
  308. package/dist/session/session-health.js +23 -0
  309. package/dist/session/session-info.d.ts +18 -0
  310. package/dist/session/session-info.js +11 -0
  311. package/dist/session/session-manager.d.ts +14 -0
  312. package/dist/session/session-manager.js +111 -4
  313. package/dist/session/session-mirror.d.ts +16 -0
  314. package/dist/session/session-mirror.js +22 -0
  315. package/dist/session/session-recommendation.d.ts +19 -5
  316. package/dist/session/session-recommendation.js +25 -9
  317. package/dist/session/session-successor.d.ts +37 -0
  318. package/dist/session/session-successor.js +113 -0
  319. package/dist/session/session-tools.js +2 -2
  320. package/dist/session/session.d.ts +53 -3
  321. package/dist/session/session.js +162 -9
  322. package/dist/session/wall-clock.d.ts +12 -0
  323. package/dist/session/wall-clock.js +14 -0
  324. package/dist/setup/agent-configs.d.ts +96 -0
  325. package/dist/setup/agent-configs.js +333 -0
  326. package/dist/setup/agent-io.d.ts +9 -0
  327. package/dist/setup/agent-io.js +16 -0
  328. package/dist/setup/agent-writer.d.ts +51 -0
  329. package/dist/setup/agent-writer.js +115 -0
  330. package/dist/setup/approval-migration.d.ts +47 -0
  331. package/dist/setup/approval-migration.js +68 -0
  332. package/dist/setup/auto-approve.d.ts +85 -0
  333. package/dist/setup/auto-approve.js +150 -0
  334. package/dist/setup/bridge-port.d.ts +15 -0
  335. package/dist/setup/bridge-port.js +24 -0
  336. package/dist/setup/desktop-shape.d.ts +57 -0
  337. package/dist/setup/desktop-shape.js +63 -0
  338. package/dist/setup/dev-server-wait.d.ts +60 -0
  339. package/dist/setup/dev-server-wait.js +65 -0
  340. package/dist/setup/drive-agent.d.ts +71 -0
  341. package/dist/setup/drive-agent.js +171 -0
  342. package/dist/setup/drive-plan.d.ts +68 -0
  343. package/dist/setup/drive-plan.js +130 -0
  344. package/dist/setup/ensure-daemon.d.ts +29 -0
  345. package/dist/setup/ensure-daemon.js +58 -0
  346. package/dist/setup/init-runtime.d.ts +47 -0
  347. package/dist/setup/init-runtime.js +160 -0
  348. package/dist/setup/interrupt.d.ts +25 -0
  349. package/dist/setup/interrupt.js +40 -0
  350. package/dist/setup/license-key.d.ts +39 -0
  351. package/dist/setup/license-key.js +72 -0
  352. package/dist/setup/listeners.d.ts +37 -0
  353. package/dist/setup/listeners.js +85 -0
  354. package/dist/setup/node-effects.d.ts +60 -0
  355. package/dist/setup/node-effects.js +210 -0
  356. package/dist/setup/page-probe.d.ts +39 -0
  357. package/dist/setup/page-probe.js +49 -0
  358. package/dist/setup/relaunch.d.ts +36 -0
  359. package/dist/setup/relaunch.js +57 -0
  360. package/dist/setup/remaining-steps.d.ts +19 -0
  361. package/dist/setup/remaining-steps.js +31 -0
  362. package/dist/setup/run-setup.d.ts +99 -0
  363. package/dist/setup/run-setup.js +187 -0
  364. package/dist/setup/session-pick.d.ts +47 -0
  365. package/dist/setup/session-pick.js +51 -0
  366. package/dist/setup/setup-command.d.ts +45 -0
  367. package/dist/setup/setup-command.js +199 -0
  368. package/dist/setup/setup-options.d.ts +74 -0
  369. package/dist/setup/setup-options.js +67 -0
  370. package/dist/setup/transcripts.d.ts +16 -0
  371. package/dist/setup/transcripts.js +43 -0
  372. package/dist/status-payload.d.ts +14 -1
  373. package/dist/status-payload.js +23 -1
  374. package/dist/telemetry/automation-hint.d.ts +2 -1
  375. package/dist/telemetry/daemon-telemetry.d.ts +8 -1
  376. package/dist/telemetry/daemon-telemetry.js +52 -5
  377. package/dist/telemetry/error-class.js +1 -1
  378. package/dist/telemetry/feedback-context.d.ts +9 -1
  379. package/dist/telemetry/feedback-context.js +60 -29
  380. package/dist/telemetry/feedback-local.d.ts +15 -0
  381. package/dist/telemetry/feedback-local.js +56 -0
  382. package/dist/telemetry/feedback-outbox.d.ts +2 -1
  383. package/dist/telemetry/feedback.d.ts +11 -0
  384. package/dist/telemetry/feedback.js +19 -6
  385. package/dist/telemetry/init-telemetry.d.ts +2 -0
  386. package/dist/telemetry/init-telemetry.js +2 -0
  387. package/dist/telemetry/install-source.d.ts +14 -0
  388. package/dist/telemetry/install-source.js +30 -0
  389. package/dist/telemetry/license-activation.d.ts +7 -1
  390. package/dist/telemetry/license-activation.js +9 -5
  391. package/dist/telemetry/mcp-connection.js +7 -0
  392. package/dist/telemetry/project-profile.d.ts +12 -9
  393. package/dist/telemetry/project-profile.js +12 -7
  394. package/dist/telemetry/proxy-telemetry.d.ts +3 -0
  395. package/dist/telemetry/proxy-telemetry.js +21 -0
  396. package/dist/telemetry/run-telemetry.d.ts +0 -2
  397. package/dist/telemetry/run-telemetry.js +1 -1
  398. package/dist/telemetry/session-metrics.d.ts +12 -0
  399. package/dist/telemetry/session-metrics.js +35 -1
  400. package/dist/telemetry/telemetry.js +17 -2
  401. package/dist/telemetry/tool-refused.d.ts +3 -1
  402. package/dist/telemetry/tool-refused.js +34 -2
  403. package/dist/telemetry/verification-of.js +2 -2
  404. package/dist/tools/act-capsule.d.ts +10 -0
  405. package/dist/tools/act-capsule.js +8 -2
  406. package/dist/tools/act-danger.d.ts +2 -1
  407. package/dist/tools/act-danger.js +11 -3
  408. package/dist/tools/act-observation.d.ts +41 -0
  409. package/dist/tools/act-observation.js +36 -0
  410. package/dist/tools/act-preflight.d.ts +20 -0
  411. package/dist/tools/act-preflight.js +98 -0
  412. package/dist/tools/act-sequence-retry.d.ts +45 -0
  413. package/dist/tools/act-sequence-retry.js +58 -0
  414. package/dist/tools/act-sequence-tool.d.ts +13 -0
  415. package/dist/tools/act-sequence-tool.js +135 -0
  416. package/dist/tools/act-target.d.ts +14 -0
  417. package/dist/tools/act-target.js +47 -0
  418. package/dist/tools/act-tools.d.ts +21 -1
  419. package/dist/tools/act-tools.js +196 -204
  420. package/dist/tools/assert-source.d.ts +22 -0
  421. package/dist/tools/assert-source.js +53 -0
  422. package/dist/tools/assert-verdict.d.ts +20 -3
  423. package/dist/tools/assert-verdict.js +58 -20
  424. package/dist/tools/browser-tools.js +6 -22
  425. package/dist/tools/complete-snapshot.d.ts +48 -0
  426. package/dist/tools/complete-snapshot.js +75 -0
  427. package/dist/tools/coverage-tools.d.ts +10 -1
  428. package/dist/tools/coverage-tools.js +123 -69
  429. package/dist/tools/dynamic-tools.js +53 -13
  430. package/dist/tools/error-recovery.d.ts +25 -14
  431. package/dist/tools/error-recovery.js +78 -9
  432. package/dist/tools/feedback-tools.d.ts +1 -1
  433. package/dist/tools/feedback-tools.js +2 -2
  434. package/dist/tools/invoke-tool.js +121 -11
  435. package/dist/tools/lease-hint.d.ts +10 -0
  436. package/dist/tools/lease-hint.js +53 -4
  437. package/dist/tools/lease-tools.d.ts +35 -5
  438. package/dist/tools/lease-tools.js +191 -13
  439. package/dist/tools/merged-name-redirect.d.ts +13 -0
  440. package/dist/tools/merged-name-redirect.js +22 -0
  441. package/dist/tools/navigate-arrival.d.ts +0 -2
  442. package/dist/tools/navigate-arrival.js +1 -1
  443. package/dist/tools/numeric-bounds.d.ts +100 -0
  444. package/dist/tools/numeric-bounds.js +125 -0
  445. package/dist/tools/observe-tools.js +109 -65
  446. package/dist/tools/read-tools.js +31 -8
  447. package/dist/tools/real-input-attempt.d.ts +32 -0
  448. package/dist/tools/real-input-attempt.js +212 -2
  449. package/dist/tools/reconcile-tools.js +22 -8
  450. package/dist/tools/resolve-target.d.ts +0 -7
  451. package/dist/tools/sdk-marker-probe.d.ts +29 -0
  452. package/dist/tools/sdk-marker-probe.js +57 -0
  453. package/dist/tools/snapshot-delta.d.ts +1 -1
  454. package/dist/tools/tool-kit.d.ts +24 -0
  455. package/dist/tools/tool-kit.js +21 -0
  456. package/dist/tools/tool-names.d.ts +5 -0
  457. package/dist/tools/tool-names.js +5 -0
  458. package/dist/tools/tool-surface.d.ts +85 -6
  459. package/dist/tools/tool-surface.js +122 -2
  460. package/dist/tools/tools-helpers.d.ts +19 -0
  461. package/dist/tools/tools-helpers.js +46 -0
  462. package/dist/tools/tools.d.ts +2 -0
  463. package/dist/tools/tools.js +78 -12
  464. package/dist/tools/unadvertised-help.js +4 -4
  465. package/dist/tools/verify-next-baton.d.ts +53 -0
  466. package/dist/tools/verify-next-baton.js +52 -0
  467. package/dist/update/reticle-deps.d.ts +9 -0
  468. package/dist/update/reticle-deps.js +27 -0
  469. package/dist/update/sdk-sync.d.ts +1 -2
  470. package/dist/update/sdk-sync.js +1 -21
  471. package/dist/update/update-nudge.d.ts +25 -10
  472. package/dist/update/update-nudge.js +32 -3
  473. package/dist/update/updater.d.ts +1 -1
  474. package/dist/version/peer-announce.js +4 -2
  475. package/dist/version/sdk-fix.d.ts +47 -0
  476. package/dist/version/sdk-fix.js +127 -0
  477. package/dist/version/version-nudge.d.ts +14 -0
  478. package/dist/version/version-nudge.js +21 -0
  479. package/dist/version/version-skew.d.ts +38 -3
  480. package/dist/version/version-skew.js +83 -7
  481. package/dist/visual/visual-store.d.ts +6 -6
  482. package/dist/visual/visual-store.js +18 -16
  483. package/dist/visual/visual-tools.js +57 -12
  484. package/docs/README.md +2 -1
  485. package/docs/cli/cloud.mdx +1 -1
  486. package/docs/cli/doctor.mdx +2 -0
  487. package/docs/cli/serve.mdx +4 -2
  488. package/docs/cli/verify.mdx +4 -5
  489. package/docs/cli.mdx +7 -3
  490. package/docs/desktop.mdx +1 -1
  491. package/docs/docs.json +2 -2
  492. package/docs/gates.md +1 -1
  493. package/docs/getting-started.md +19 -3
  494. package/docs/index.mdx +9 -6
  495. package/docs/install-agentic.mdx +38 -12
  496. package/docs/install-manual.mdx +1 -1
  497. package/docs/packages/browser.mdx +2 -2
  498. package/docs/packages/server.mdx +1 -1
  499. package/docs/packages.mdx +8 -6
  500. package/docs/platform-integration.md +17 -0
  501. package/docs/predicates.mdx +8 -0
  502. package/docs/reticle-manual.md +106 -0
  503. package/docs/skill-file.mdx +3 -3
  504. package/docs/telemetry-contract.md +108 -4
  505. package/docs/tools-act-sequence.mdx +16 -0
  506. package/docs/tools-tools-and-run.mdx +2 -4
  507. package/docs/troubleshooting.mdx +9 -0
  508. package/docs/usage.md +4 -3
  509. package/package.json +4 -3
  510. package/dist/init/cursor.d.ts +0 -24
  511. package/dist/init/cursor.js +0 -80
  512. package/dist/session/session-readiness.d.ts +0 -34
  513. package/dist/session/session-readiness.js +0 -36
@@ -123,6 +123,12 @@ export class SessionMetrics {
123
123
  * call a tool, and of the sessions that made exactly one call, most bounced on this.
124
124
  */
125
125
  #noSessionErrors = 0;
126
+ /** Connection-level MCP POST failures the SSE stream cannot see. See SessionSummary. */
127
+ #postSocketFailures = 0;
128
+ #lifetimePostSocketFailures = 0;
129
+ /** Retries of those POSTs that then delivered. */
130
+ #postRetriesSaved = 0;
131
+ #lifetimePostRetriesSaved = 0;
126
132
  /** Longest back-to-back run per tool — a retry loop, which `toolCounts` cannot distinguish. */
127
133
  #repeatRuns = new Map();
128
134
  #lastTool;
@@ -325,6 +331,24 @@ export class SessionMetrics {
325
331
  tool: site,
326
332
  });
327
333
  }
334
+ /**
335
+ * The MCP proxy could not POST a tool call because the TCP socket itself failed (`ENOBUFS`,
336
+ * `EMFILE`, `ECONNREFUSED` before any bytes left). The SSE stream is still up, so this is
337
+ * invisible to `mcp_connection_lost`, and the handler never ran, so it is invisible to
338
+ * `tool_refused`. Counted so a dashboard can see it without a new event kind.
339
+ */
340
+ recordPostSocketFailure() {
341
+ this.#postSocketFailures += 1;
342
+ this.#lifetimePostSocketFailures += 1;
343
+ }
344
+ /**
345
+ * A bounded retry of an unsent POST then delivered. The pair against `recordPostSocketFailure`:
346
+ * the retry that quietly saved a call is a different fact from the one that never ran.
347
+ */
348
+ recordPostRetrySaved() {
349
+ this.#postRetriesSaved += 1;
350
+ this.#lifetimePostRetriesSaved += 1;
351
+ }
328
352
  /** The agent's recent approach run + the call in flight — context for a crash report. */
329
353
  get trail() {
330
354
  return { breadcrumb: [...this.#breadcrumb], inFlight: this.#inFlight };
@@ -469,12 +493,16 @@ export class SessionMetrics {
469
493
  toolErrors: this.#lifetimeToolErrors,
470
494
  verifications: this.#lifetimeVerifications,
471
495
  bugsFound: this.#lifetimeBugsFound,
496
+ postSocketFailures: this.#lifetimePostSocketFailures,
497
+ postRetriesSaved: this.#lifetimePostRetriesSaved,
472
498
  }
473
499
  : {
474
500
  toolCalls: this.#toolCalls,
475
501
  toolErrors: this.#toolErrors,
476
502
  verifications: this.#verifications,
477
503
  bugsFound: this.#bugsFound,
504
+ postSocketFailures: this.#postSocketFailures,
505
+ postRetriesSaved: this.#postRetriesSaved,
478
506
  };
479
507
  return {
480
508
  durationMs: Math.max(0, this.#now() - this.#startedAt),
@@ -488,6 +516,8 @@ export class SessionMetrics {
488
516
  bugsFound: scalars.bugsFound,
489
517
  ...(this.#bugKinds.size > 0 ? { bugKinds: Object.fromEntries(this.#bugKinds) } : {}),
490
518
  ...(this.#noSessionErrors > 0 ? { noSessionErrors: this.#noSessionErrors } : {}),
519
+ ...(scalars.postSocketFailures > 0 ? { postSocketFailures: scalars.postSocketFailures } : {}),
520
+ ...(scalars.postRetriesSaved > 0 ? { postRetriesSaved: scalars.postRetriesSaved } : {}),
491
521
  ...(this.#repeatRuns.size > 0
492
522
  ? { consecutiveRepeats: Object.fromEntries(this.#repeatRuns) }
493
523
  : {}),
@@ -588,7 +618,9 @@ export class SessionMetrics {
588
618
  0 === this.#verifications &&
589
619
  0 === this.#toolErrors &&
590
620
  0 === this.#bugsFound &&
591
- 0 === this.#sdkFailures);
621
+ 0 === this.#sdkFailures &&
622
+ 0 === this.#postSocketFailures &&
623
+ 0 === this.#postRetriesSaved);
592
624
  }
593
625
  /** Zero the counters after a non-final flush so the next flush reports the NEXT window, not a total. */
594
626
  reset() {
@@ -596,6 +628,8 @@ export class SessionMetrics {
596
628
  this.#toolErrors = 0;
597
629
  this.#verifications = 0;
598
630
  this.#noSessionErrors = 0;
631
+ this.#postSocketFailures = 0;
632
+ this.#postRetriesSaved = 0;
599
633
  this.#repeatRuns.clear();
600
634
  this.#lastTool = undefined;
601
635
  this.#currentRun = 0;
@@ -9,7 +9,7 @@
9
9
  * - it is best-effort and non-blocking: a send failure NEVER changes what the tool does.
10
10
  *
11
11
  * Events are `@reticlehq/core`'s `TelemetryEventSchema`, mapped at the wire into PostHog's capture
12
- * format (https://posthog.com/docs/api/capture) — PostHog is the analytics backend until Reticle Cloud
12
+ * format (https://posthog.com/docs/api/capture) — PostHog is the analytics backend until the hosted service
13
13
  * grows its own; a project API key is WRITE-ONLY by design, so embedding it in an OSS client is safe.
14
14
  * Everything here is wrapped so a telemetry bug can never surface to a user — a broken metric must not
15
15
  * break a verification.
@@ -76,8 +76,23 @@ export const FEEDBACK_RETRY_BACKOFF_MS = 750;
76
76
  * (`reticle version`/`gate`) ~800ms. Long-lived daemon events still send in-process: a spawn per tool
77
77
  * call would be far heavier than a fetch inside an already-running server.
78
78
  */
79
+ /**
80
+ * The budget the DISOWNED child gets, and why it is not `SEND_TIMEOUT_MS`.
81
+ *
82
+ * 2s is right for an in-process send: the daemon is waiting on it, so the budget is a latency cost
83
+ * somebody pays. A detached child pays nobody — the parent has already exited — so the only thing a
84
+ * short budget there buys is a lost event.
85
+ *
86
+ * And it loses the worst ones. Every detached send comes from a SHORT-LIVED CLI process, which pays
87
+ * cold DNS and a cold TLS route by definition; measured on the feedback path with a WARM cache it
88
+ * was already 0.694s before any payload moved. `reticle_installed` is the extreme case: it fires
89
+ * once per machine, on the first Reticle command that machine ever runs, which is the single coldest
90
+ * network call it will ever make to the collector — so the top of the funnel is the event most
91
+ * likely to be dropped, and it fails silently like everything else here.
92
+ */
93
+ const DETACHED_SEND_TIMEOUT_MS = 15_000;
79
94
  const DETACHED_SEND_SCRIPT = "fetch(process.argv[1],{method:'POST',headers:{'content-type':'application/json'}," +
80
- `body:process.argv[2],signal:AbortSignal.timeout(${SEND_TIMEOUT_MS})})` +
95
+ `body:process.argv[2],signal:AbortSignal.timeout(${DETACHED_SEND_TIMEOUT_MS})})` +
81
96
  '.catch(()=>{}).finally(()=>process.exit(0))';
82
97
  /** Env var names — mirror cloud-sync's `RETICLE_*` convention. */
83
98
  import { isReticleSourceCheckout } from './dev-repo.js';
@@ -10,7 +10,9 @@
10
10
  * Emitted from `runTool`, which is the one place both dispatch paths cross, so a tool added later is
11
11
  * covered without anybody remembering to cover it.
12
12
  */
13
- import { type RefusalReason } from '@reticlehq/core';
13
+ import { RefusalReason, type NoSessionReason } from '@reticlehq/core';
14
+ /** Record the branch code for the no-session error being thrown right now. */
15
+ export declare function notePendingNoSessionReason(reason: NoSessionReason | undefined): void;
14
16
  /** A call that was served. Breaks the retry chain: what follows it is not a retry of anything. */
15
17
  export declare function noteToolServed(): void;
16
18
  /** One refused call, reported with its cause and whether it was itself a retry. */
@@ -10,7 +10,7 @@
10
10
  * Emitted from `runTool`, which is the one place both dispatch paths cross, so a tool added later is
11
11
  * covered without anybody remembering to cover it.
12
12
  */
13
- import { TelemetryActor, TelemetryEventKind } from '@reticlehq/core';
13
+ import { RefusalReason, TelemetryActor, TelemetryEventKind, } from '@reticlehq/core';
14
14
  import { getTelemetry } from './telemetry.js';
15
15
  /**
16
16
  * Refusal events one daemon run will send.
@@ -32,6 +32,24 @@ let sent = 0;
32
32
  * describe — so the flag lands on the RETRY instead of on the first refusal.
33
33
  */
34
34
  let lastRefusedTool;
35
+ /**
36
+ * The no-session branch code for the refusal about to be reported, set by whoever threw it.
37
+ *
38
+ * `no_session` is the largest refusal cohort and on its own it is a set difference: nothing
39
+ * connected, and no word on which of several opposite situations that was. The diagnosis that CAN
40
+ * say is computed where the error is thrown, and the refusal is classified downstream from the
41
+ * message, so the two never met (#615).
42
+ *
43
+ * Module-level and consumed once, like `lastRefusedTool` above and for the same reason: the
44
+ * producer and the consumer are one hop apart on the same call, and threading a field through every
45
+ * refusal path to carry it would touch a dozen call sites that have nothing to do with sessions.
46
+ * Cleared on read so a later refusal cannot inherit a stale reason.
47
+ */
48
+ let pendingNoSessionReason;
49
+ /** Record the branch code for the no-session error being thrown right now. */
50
+ export function notePendingNoSessionReason(reason) {
51
+ pendingNoSessionReason = reason;
52
+ }
35
53
  /** A call that was served. Breaks the retry chain: what follows it is not a retry of anything. */
36
54
  export function noteToolServed() {
37
55
  lastRefusedTool = undefined;
@@ -40,16 +58,30 @@ export function noteToolServed() {
40
58
  export function reportToolRefused(tool, reason) {
41
59
  const retried = lastRefusedTool === tool;
42
60
  lastRefusedTool = tool;
61
+ // Read and clear whatever the throw left, whether or not this refusal can use it: a reason left
62
+ // sitting would attach itself to an unrelated refusal later in the session.
63
+ const noSessionReason = pendingNoSessionReason;
64
+ pendingNoSessionReason = undefined;
43
65
  if (sent >= MAX_REFUSALS_PER_SESSION)
44
66
  return;
45
67
  sent += 1;
46
68
  void getTelemetry().emit(TelemetryEventKind.TOOL_REFUSED, {
47
69
  actor: TelemetryActor.AGENT,
48
- refusal: { tool, reason, retried },
70
+ refusal: {
71
+ tool,
72
+ reason,
73
+ retried,
74
+ // Only where it means something. On any other refusal reason this field would be answering
75
+ // a question nobody asked of it.
76
+ ...(RefusalReason.NO_SESSION === reason && noSessionReason !== undefined
77
+ ? { noSessionReason }
78
+ : {}),
79
+ },
49
80
  });
50
81
  }
51
82
  /** Tests only — the counters are process-lifetime, so each case has to start from zero. */
52
83
  export function resetToolRefusals() {
53
84
  sent = 0;
54
85
  lastRefusedTool = undefined;
86
+ pendingNoSessionReason = undefined;
55
87
  }
@@ -13,7 +13,7 @@
13
13
  * be readable and testable on its own rather than inferred from a ternary inside a dispatcher.
14
14
  */
15
15
  import { CaptureLoss, VerifiedReason } from '@reticlehq/core';
16
- import { VERIFICATION_TOOLS } from '../tools/feedback-tools.js';
16
+ import { VERDICT_TOOLS } from '../tools/feedback-tools.js';
17
17
  import { getBrowserMode } from './browser-mode.js';
18
18
  /**
19
19
  * The deciding clause, read off the result `decideVerified` wrote it onto.
@@ -92,7 +92,7 @@ function passedOf(result, status) {
92
92
  * it verified. That is the product's thesis reduced to a boolean.
93
93
  */
94
94
  export function verificationOf(toolName, result, durationMs, brand) {
95
- if (!VERIFICATION_TOOLS.has(toolName))
95
+ if (!VERDICT_TOOLS.has(toolName))
96
96
  return undefined;
97
97
  // A paused session REFUSES the call — nothing driven, nothing asserted. The refusal carries a
98
98
  // verdict field so the agent never reads undefined off it, and that field must not be mistaken
@@ -17,6 +17,16 @@ interface CapsuleSaveInputs {
17
17
  file: string;
18
18
  line: number;
19
19
  };
20
+ /**
21
+ * The `.reticle` this capsule belongs in, from the session the act ACTUALLY drove.
22
+ *
23
+ * Passed in rather than re-resolved here, because re-resolving loses twice: `args.sessionId` is
24
+ * frequently absent, and `sessions.resolve(undefined)` throws whenever more than one tab is
25
+ * connected — which is precisely the multi-project case this routing exists for, so it failed in
26
+ * exactly the situation it was written to fix. The caller also knows about a session the act
27
+ * FOLLOWED through a navigation; this file cannot.
28
+ */
29
+ root?: string | undefined;
20
30
  }
21
31
  /** Returns the capsule id when one was written, or undefined when there was nothing to save. */
22
32
  export declare function saveFailedAssertCapsule(inputs: CapsuleSaveInputs): Promise<string | undefined>;
@@ -13,16 +13,22 @@ import { ActionType, AnchorKind } from '@reticlehq/core';
13
13
  import { CapsuleStore, capsuleId, CAPSULE_VERSION } from '../capsule/capsule-store.js';
14
14
  import { ReticleTool } from './tool-names.js';
15
15
  import { asRecord, asString } from './tools-helpers.js';
16
+ import { sessionRoot } from '../project/session-root.js';
16
17
  /** Returns the capsule id when one was written, or undefined when there was nothing to save. */
17
18
  export async function saveFailedAssertCapsule(inputs) {
18
- const { deps, verdict, capsule, links, args, actResult, actedSource } = inputs;
19
+ const { deps, verdict, capsule, links, args, actResult, actedSource, root: given } = inputs;
19
20
  if (verdict.pass || capsule === undefined)
20
21
  return undefined;
21
22
  const id = capsuleId(deps.now(), asString(args['ref']) ?? 'assert');
22
23
  const expectedText = links
23
24
  .map((l) => ('name' in l ? `${l.kind} ${String(l.name)}` : l.kind))
24
25
  .join(' AND ');
25
- const saved = await new CapsuleStore(deps.fs, deps.reticleRoot).save({
26
+ // The capsule belongs to the project whose app just failed, not to wherever the daemon stands.
27
+ // Same rule and same resolver as every other artifact: a capsule written into a sibling repo is
28
+ // evidence filed against a codebase that did not produce it, and it outlives the turn.
29
+ // The caller's answer wins; the resolver is the fallback for callers that have no session.
30
+ const root = given ?? sessionRoot(deps, asString(args['sessionId']));
31
+ const saved = await new CapsuleStore(deps.fs, root).save({
26
32
  version: CAPSULE_VERSION,
27
33
  id,
28
34
  createdAt: deps.now(),
@@ -2,7 +2,8 @@ import { ActionType } from '@reticlehq/core';
2
2
  export declare function assertNotDestructive(action: ActionType, innerArgs: Record<string, unknown>, inspected: unknown): void;
3
3
  /**
4
4
  * A drag is judged on BOTH ends. Dropping a harmless row onto "Delete" is destructive, and the
5
- * source alone never says so.
5
+ * source alone never says so. Each end is classified on its own text and role, so a Payment
6
+ * option dragged onto Save is not a payment.
6
7
  */
7
8
  export declare function assertDragNotDestructive(innerArgs: Record<string, unknown>, from: unknown, to: unknown): void;
8
9
  /**
@@ -22,23 +22,31 @@ function descriptorText(value) {
22
22
  asString(descriptor['formText']) ?? '',
23
23
  ].join(' ');
24
24
  }
25
+ function descriptorRole(value) {
26
+ const role = asString(asRecord(value)['role']);
27
+ return role !== undefined && role.length > 0 ? role : undefined;
28
+ }
29
+ function isDestructiveDescriptor(value) {
30
+ return isDangerousActionText(descriptorText(value), descriptorRole(value));
31
+ }
25
32
  export function assertNotDestructive(action, innerArgs, inspected) {
26
33
  if (action !== ActionType.CLICK && action !== ActionType.DBLCLICK)
27
34
  return;
28
35
  if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
29
36
  return;
30
- if (!isDangerousActionText(descriptorText(inspected)))
37
+ if (!isDestructiveDescriptor(inspected))
31
38
  return;
32
39
  throw new Error(`potentially destructive native action blocked; retry with args.${DANGEROUS_ACTION_CONFIRM_ARG}=true`);
33
40
  }
34
41
  /**
35
42
  * A drag is judged on BOTH ends. Dropping a harmless row onto "Delete" is destructive, and the
36
- * source alone never says so.
43
+ * source alone never says so. Each end is classified on its own text and role, so a Payment
44
+ * option dragged onto Save is not a payment.
37
45
  */
38
46
  export function assertDragNotDestructive(innerArgs, from, to) {
39
47
  if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
40
48
  return;
41
- if (!isDangerousActionText(`${descriptorText(from)} ${descriptorText(to)}`))
49
+ if (!isDestructiveDescriptor(from) && !isDestructiveDescriptor(to))
42
50
  return;
43
51
  throw new Error(`potentially destructive native action blocked; retry with args.${DANGEROUS_ACTION_CONFIRM_ARG}=true`);
44
52
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * What to do when the observer left in the middle of an act.
3
+ *
4
+ * A full-document navigation — often the very thing the act CAUSED — tears the SDK down. The page
5
+ * re-announces, and whatever this call still had in flight is gone. That is a fact about the
6
+ * observer, never about the application, and the difference matters: every honest verdict this
7
+ * engine can give depends on not converting "I stopped watching" into "your app is broken".
8
+ *
9
+ * Extracted from act-tools.ts, which had reached the file-size cap. It earns its own module because
10
+ * it is one idea — follow the document that took over, and re-ask there — used from the two points
11
+ * where observation can be lost: a write rejected at dispatch, and a wait cut off mid-predicate.
12
+ */
13
+ import type { Session } from '../session/session.js';
14
+ import { type SuccessorRegistry } from '../session/session-successor.js';
15
+ import type { EvalResult } from '../events/predicate-eval.js';
16
+ export interface FollowSuccessor {
17
+ sessions: SuccessorRegistry;
18
+ session: Session;
19
+ verdict: EvalResult;
20
+ /** Total budget the CALLER granted, in ms. */
21
+ timeout: number;
22
+ /** `session.elapsed()` at the moment the predicate started. */
23
+ predicateStarted: number;
24
+ /** Re-ask the declared consequence on `next`, with whatever budget is left. */
25
+ reevaluate: (next: Session, budgetMs: number) => Promise<EvalResult>;
26
+ }
27
+ export interface FollowedOutcome {
28
+ session: Session;
29
+ verdict: EvalResult;
30
+ /** True when the wait moved to a new document, so the caller must reset its event cursor. */
31
+ followed: boolean;
32
+ }
33
+ /**
34
+ * Follow the unique same-origin successor and re-ask there, or leave the verdict untouched.
35
+ *
36
+ * `navigate` already waits for the HELLO and returns the new id; this path used to grade
37
+ * `observation_lost` and leave the agent holding a DEAD id, so the next assert failed even though
38
+ * the new page had loaded perfectly. Two live tabs at that origin is still a guess, and
39
+ * `awaitDocumentSuccessor` refuses to guess — a wrong follow drives somebody else's app.
40
+ */
41
+ export declare function followLostObservation(input: FollowSuccessor): Promise<FollowedOutcome>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * What to do when the observer left in the middle of an act.
3
+ *
4
+ * A full-document navigation — often the very thing the act CAUSED — tears the SDK down. The page
5
+ * re-announces, and whatever this call still had in flight is gone. That is a fact about the
6
+ * observer, never about the application, and the difference matters: every honest verdict this
7
+ * engine can give depends on not converting "I stopped watching" into "your app is broken".
8
+ *
9
+ * Extracted from act-tools.ts, which had reached the file-size cap. It earns its own module because
10
+ * it is one idea — follow the document that took over, and re-ask there — used from the two points
11
+ * where observation can be lost: a write rejected at dispatch, and a wait cut off mid-predicate.
12
+ */
13
+ import { awaitDocumentSuccessor } from '../session/session-successor.js';
14
+ /**
15
+ * Follow the unique same-origin successor and re-ask there, or leave the verdict untouched.
16
+ *
17
+ * `navigate` already waits for the HELLO and returns the new id; this path used to grade
18
+ * `observation_lost` and leave the agent holding a DEAD id, so the next assert failed even though
19
+ * the new page had loaded perfectly. Two live tabs at that origin is still a guess, and
20
+ * `awaitDocumentSuccessor` refuses to guess — a wrong follow drives somebody else's app.
21
+ */
22
+ export async function followLostObservation(input) {
23
+ const { sessions, session, verdict, timeout, predicateStarted, reevaluate } = input;
24
+ if (true !== verdict.observationLost || 0 >= timeout)
25
+ return { session, verdict, followed: false };
26
+ const remaining = timeout - (session.elapsed() - predicateStarted);
27
+ const next = remaining > 0 ? await awaitDocumentSuccessor(sessions, session, remaining) : null;
28
+ if (null === next)
29
+ return { session, verdict, followed: false };
30
+ // Computed on the DEPARTED session, before the caller points at the successor: the new session's
31
+ // elapsed() starts at 0 and would silently refund the whole budget the caller had already spent.
32
+ const leftover = timeout - (session.elapsed() - predicateStarted);
33
+ if (leftover <= 0)
34
+ return { session: next, verdict, followed: true };
35
+ return { session: next, verdict: await reevaluate(next, leftover), followed: true };
36
+ }
@@ -0,0 +1,20 @@
1
+ export declare function assertSequenceSteps(steps: readonly unknown[]): void;
2
+ /**
3
+ * Throws if this call cannot honestly be driven. Call it after the args are parsed and before the
4
+ * first dispatch — anything that depends on what is actually rendered belongs after the action.
5
+ */
6
+ export declare function preflightAct(actArgs: Record<string, unknown>, until: unknown): void;
7
+ /**
8
+ * Dispatch a write, or report that its outcome went unobserved.
9
+ *
10
+ * Returns null — never throws — when the transport was displaced by a newer connection claiming the
11
+ * same id. That happens on any full-document navigation the act itself causes: the page unloads,
12
+ * re-announces, and the write in flight is rejected by the handle that just died.
13
+ *
14
+ * The write is deliberately NOT re-sent. Re-asking a READ on the successor is free; re-sending a
15
+ * WRITE is not, because nothing can prove the first one did not reach the page — and a duplicated
16
+ * click on a button that charges a card is a far worse outcome than a verdict of "I could not see".
17
+ * Every other failure propagates unchanged: only a replacement is an observation problem, and
18
+ * treating a timeout or a refusal as one would launder a real failure into a shrug.
19
+ */
20
+ export declare function dispatchAct<T>(send: () => Promise<T>): Promise<T | null>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Everything refused BEFORE the action window opens.
3
+ *
4
+ * Both checks here share one property, and it is the reason they belong together: each is decidable
5
+ * without touching the page, and each would otherwise be discovered only after the click had landed
6
+ * — when the action is spent, the page has moved, and the honest answer has already been lost.
7
+ *
8
+ * Refusing early is not a convenience. `reticle_act_and_wait` promises a verdict, and a verdict that
9
+ * blames the app for the caller's own mistake is the most damaging thing a verification tool can
10
+ * produce: it sends somebody to fix code that is not broken. "Nothing was acted on" is a far better
11
+ * outcome than "unknown".
12
+ */
13
+ import { SessionReplacedError } from '../session/pending-commands.js';
14
+ import { assertNativeInputSupported } from './act-danger.js';
15
+ import { unevaluablePredicateReason } from '../events/predicate-precheck.js';
16
+ /**
17
+ * Refuse a sequence whose steps cannot be addressed.
18
+ *
19
+ * `steps` is a bare array of objects, so a step with neither `ref` nor `target` used to travel on
20
+ * and dispatch as `ref: ''`. The browser then threw `ref '' no longer resolves to an element` —
21
+ * a stale-ref diagnosis for a missing locator — and the caller went looking for a re-render.
22
+ *
23
+ * `target` is the same locator `reticle_act` / `reticle_act_and_wait` take; the handler resolves it
24
+ * with `resolveActTarget` before dispatch. This check only asks that every step names one of the
25
+ * two, and it runs before the first step, so a typo in step three cannot leave one and two applied.
26
+ */
27
+ /**
28
+ * Keys an agent reaches for when it means "and prove this happened".
29
+ *
30
+ * A sub-step reads `ref`/`target`/`action`/`args` and nothing else, and the schema is a permissive
31
+ * record, so any of these was accepted and dropped. `until` is the likeliest of them by a distance:
32
+ * it is what act_and_wait — the tool immediately next to this one — calls its assertion.
33
+ *
34
+ * Silently ignoring one manufactures a false green. A sequence carrying an impossible `until` came
35
+ * back `completed: 1` with no error and nothing said about the predicate, which reads as the
36
+ * consequence having held. act_sequence cannot grade one; refusing and naming the tool that can is
37
+ * the same answer given to an unsupported native click, and for the same reason.
38
+ */
39
+ const CONSEQUENCE_KEYS = ['until', 'expect', 'assert', 'waitFor'];
40
+ export function assertSequenceSteps(steps) {
41
+ if (0 === steps.length) {
42
+ throw new Error('reticle_act_sequence was given no steps. Pass steps: [{ ref, action, args? }] or [{ target, action, args? }] — nothing was acted on.');
43
+ }
44
+ steps.forEach((raw, i) => {
45
+ const step = 'object' === typeof raw && null !== raw ? raw : {};
46
+ const claimed = CONSEQUENCE_KEYS.find((k) => step[k] !== undefined);
47
+ if (claimed !== undefined) {
48
+ throw new Error(`step ${String(i)} carries \`${claimed}\`, and reticle_act_sequence cannot grade it. ` +
49
+ 'Sub-steps are dispatched, never asserted — the key would be dropped and the sequence ' +
50
+ 'would report success having checked nothing. Drive the steps here, then prove the ' +
51
+ 'consequence with reticle_act_and_wait on the LAST action (its `until` is graded), or ' +
52
+ 'with reticle_assert after this call. Nothing was acted on.');
53
+ }
54
+ if ('string' === typeof step['ref'] && step['ref'].length > 0)
55
+ return;
56
+ if (step['target'] !== undefined)
57
+ return;
58
+ throw new Error(`step ${String(i)} has no \`ref\` or \`target\`. ` +
59
+ 'Sequence steps take `ref` (from reticle_query/reticle_snapshot) or `target` ' +
60
+ '(e.g. { testid } or { label }), the same locator reticle_act accepts. ' +
61
+ 'Nothing was acted on — the whole sequence is refused so a bad step cannot leave the earlier ones half-applied.');
62
+ });
63
+ }
64
+ /**
65
+ * Throws if this call cannot honestly be driven. Call it after the args are parsed and before the
66
+ * first dispatch — anything that depends on what is actually rendered belongs after the action.
67
+ */
68
+ export function preflightAct(actArgs, until) {
69
+ // This path cannot honour a native-input request, and taking the argument and ignoring it told
70
+ // the agent its trusted click had happened. See act-danger.
71
+ assertNativeInputSupported(actArgs);
72
+ const unevaluable = unevaluablePredicateReason(until);
73
+ if (unevaluable !== undefined)
74
+ throw new Error(unevaluable);
75
+ }
76
+ /**
77
+ * Dispatch a write, or report that its outcome went unobserved.
78
+ *
79
+ * Returns null — never throws — when the transport was displaced by a newer connection claiming the
80
+ * same id. That happens on any full-document navigation the act itself causes: the page unloads,
81
+ * re-announces, and the write in flight is rejected by the handle that just died.
82
+ *
83
+ * The write is deliberately NOT re-sent. Re-asking a READ on the successor is free; re-sending a
84
+ * WRITE is not, because nothing can prove the first one did not reach the page — and a duplicated
85
+ * click on a button that charges a card is a far worse outcome than a verdict of "I could not see".
86
+ * Every other failure propagates unchanged: only a replacement is an observation problem, and
87
+ * treating a timeout or a refusal as one would launder a real failure into a shrug.
88
+ */
89
+ export async function dispatchAct(send) {
90
+ try {
91
+ return await send();
92
+ }
93
+ catch (error) {
94
+ if (error instanceof SessionReplacedError)
95
+ return null;
96
+ throw error;
97
+ }
98
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The two things a sequence step needs beyond a plain act: a retry when the ref goes stale under a
3
+ * re-render, and a result shaped for the per-step report.
4
+ *
5
+ * ── THE RETRY ─────────────────────────────────────────────────────────────────────────────────
6
+ *
7
+ * Refs are invalidated by a re-render, and `reticle_act_sequence` exists precisely because the
8
+ * caller cannot re-snapshot between steps. So `fill -> fill -> submit` fails on step two the moment
9
+ * step one causes React to re-render — and the SAME refs then work when the caller retries the whole
10
+ * sequence by hand, which is the tell: this is a race with the render, not a dead element. Losing
11
+ * the sequence to it makes the tool unusable for the exact shape it was built for.
12
+ *
13
+ * The fix is one retry, after the app has been given the grace period it already gets elsewhere, and
14
+ * ONLY for staleness. A genuinely gone element fails again immediately, and a step that failed for
15
+ * any other reason is never retried — a tool that quietly re-runs actions would turn one click into
16
+ * two, which is worse than the failure it was papering over.
17
+ */
18
+ import { waitForReaction } from './react-grace.js';
19
+ export declare const isStaleRefError: (error: string | undefined) => boolean;
20
+ /** The shape a step attempt returns. Structural so this file needs nothing from the tool layer. */
21
+ export interface StepOutcome {
22
+ ok: boolean;
23
+ error?: string | undefined;
24
+ result?: unknown;
25
+ }
26
+ /** Just enough of a session for the grace period. Same structural contract react-grace.ts uses. */
27
+ type ReactionSource = Parameters<typeof waitForReaction>[0];
28
+ /**
29
+ * Run one step, and retry it once if — and only if — its ref went stale under a re-render.
30
+ *
31
+ * `attempt` is called at most twice with identical arguments, so it must be the same idempotent
32
+ * dispatch the caller would have made itself.
33
+ */
34
+ export declare function runStepWithStaleRetry(attempt: () => Promise<StepOutcome>, session: ReactionSource, since: number, budgetMs: number, opts?: {
35
+ sleep(ms: number): Promise<void>;
36
+ }): Promise<StepOutcome>;
37
+ /**
38
+ * One step's entry in the per-step report.
39
+ *
40
+ * Only the fields the underlying act actually produced. Copying them conditionally keeps a clean step
41
+ * from carrying a row of nulls that read as "we looked and found nothing" rather than "there was
42
+ * nothing to look for".
43
+ */
44
+ export declare function describeStepResult(step: Record<string, unknown>, result: Record<string, unknown>): Record<string, unknown>;
45
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The two things a sequence step needs beyond a plain act: a retry when the ref goes stale under a
3
+ * re-render, and a result shaped for the per-step report.
4
+ *
5
+ * ── THE RETRY ─────────────────────────────────────────────────────────────────────────────────
6
+ *
7
+ * Refs are invalidated by a re-render, and `reticle_act_sequence` exists precisely because the
8
+ * caller cannot re-snapshot between steps. So `fill -> fill -> submit` fails on step two the moment
9
+ * step one causes React to re-render — and the SAME refs then work when the caller retries the whole
10
+ * sequence by hand, which is the tell: this is a race with the render, not a dead element. Losing
11
+ * the sequence to it makes the tool unusable for the exact shape it was built for.
12
+ *
13
+ * The fix is one retry, after the app has been given the grace period it already gets elsewhere, and
14
+ * ONLY for staleness. A genuinely gone element fails again immediately, and a step that failed for
15
+ * any other reason is never retried — a tool that quietly re-runs actions would turn one click into
16
+ * two, which is worse than the failure it was papering over.
17
+ */
18
+ import { waitForReaction } from './react-grace.js';
19
+ /** Matches the daemon's own wording, which error-recovery.ts also keys its STALE_REF hint off. */
20
+ const STALE_REF = /no longer resolves to an element/i;
21
+ export const isStaleRefError = (error) => error !== undefined && STALE_REF.test(error);
22
+ const realSleep = {
23
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
24
+ };
25
+ /**
26
+ * Run one step, and retry it once if — and only if — its ref went stale under a re-render.
27
+ *
28
+ * `attempt` is called at most twice with identical arguments, so it must be the same idempotent
29
+ * dispatch the caller would have made itself.
30
+ */
31
+ export async function runStepWithStaleRetry(attempt, session, since, budgetMs, opts = realSleep) {
32
+ const first = await attempt();
33
+ if (first.ok || !isStaleRefError(first.error))
34
+ return first;
35
+ await waitForReaction(session, since, budgetMs, opts);
36
+ return attempt();
37
+ }
38
+ /**
39
+ * One step's entry in the per-step report.
40
+ *
41
+ * Only the fields the underlying act actually produced. Copying them conditionally keeps a clean step
42
+ * from carrying a row of nulls that read as "we looked and found nothing" rather than "there was
43
+ * nothing to look for".
44
+ */
45
+ export function describeStepResult(step, result) {
46
+ const out = {
47
+ ref: result['ref'] ?? step['ref'],
48
+ action: result['action'] ?? step['action'],
49
+ dispatched: result['dispatched'] ?? true,
50
+ settled: result['settled'] ?? null,
51
+ settleReason: result['settleReason'] ?? null,
52
+ };
53
+ for (const key of ['testid', 'component', 'role', 'name', 'source', 'warning']) {
54
+ if (result[key] !== undefined)
55
+ out[key] = result[key];
56
+ }
57
+ return out;
58
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `reticle_act_sequence` — several actions compiled into one round trip.
3
+ *
4
+ * Split out of `act-tools.ts` when that file passed the 1000-line backstop. A tool seam rather than
5
+ * an arbitrary one: its two neighbours drive ONE action and grade its consequence, while this one
6
+ * compiles a list of steps and runs them inside a single action window, reporting per step.
7
+ *
8
+ * It keeps its own `beginAction` — the dispatch and the attribution window that must wrap it stay in
9
+ * the same file, which is the property `dispatch-attribution.test.ts` enforces. An earlier attempt
10
+ * split the shared `actCommand` helper out instead, which separated the two and that guard caught it.
11
+ */
12
+ import { type ToolDef } from './tool-kit.js';
13
+ export declare const ACT_SEQUENCE_TOOL: ToolDef;