@reticlehq/server 2.4.0 → 2.6.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 (472) hide show
  1. package/SKILL.md +719 -0
  2. package/dist/bridge/auth-failure-reason.d.ts +19 -0
  3. package/dist/bridge/auth-failure-reason.js +29 -0
  4. package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
  5. package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
  6. package/dist/{bridge.d.ts → bridge/bridge.d.ts} +13 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +95 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
  10. package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
  11. package/dist/capsule/capsule-store.d.ts +8 -3
  12. package/dist/capsule/capsule-store.js +7 -2
  13. package/dist/capsule/capsule.js +3 -3
  14. package/dist/capsule/causal-summary.d.ts +8 -0
  15. package/dist/capsule/causal-summary.js +22 -12
  16. package/dist/capsule/divergence.d.ts +9 -4
  17. package/dist/capsule/divergence.js +8 -8
  18. package/dist/capsule/predicate-to-links.js +11 -8
  19. package/dist/cli/cli-doctor.d.ts +1 -0
  20. package/dist/cli/cli-doctor.js +135 -0
  21. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  22. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  23. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  24. package/dist/cli/cli-launch.js +171 -0
  25. package/dist/cli/cli-parse-feedback.d.ts +31 -0
  26. package/dist/cli/cli-parse-feedback.js +56 -0
  27. package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
  28. package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
  29. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
  30. package/dist/{cli-port.js → cli/cli-port.js} +9 -9
  31. package/dist/cli/cli-update-commands.d.ts +4 -0
  32. package/dist/cli/cli-update-commands.js +93 -0
  33. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  34. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  35. package/dist/cli/doctor-daemon-line.d.ts +37 -0
  36. package/dist/cli/doctor-daemon-line.js +35 -0
  37. package/dist/cli/port-holder.d.ts +43 -0
  38. package/dist/cli/port-holder.js +99 -0
  39. package/dist/cli.d.ts +2 -2
  40. package/dist/cli.js +302 -100
  41. package/dist/cloud/cloud-config.d.ts +0 -1
  42. package/dist/cloud/cloud-config.js +9 -9
  43. package/dist/cloud/cloud-sync.d.ts +1 -7
  44. package/dist/cloud/cloud-sync.js +12 -12
  45. package/dist/crawl/crawl-empty.d.ts +16 -0
  46. package/dist/crawl/crawl-empty.js +24 -0
  47. package/dist/crawl/crawl-tools.js +4 -1
  48. package/dist/crawl/crawl.js +26 -7
  49. package/dist/daemon/daemon-resilience.d.ts +73 -0
  50. package/dist/daemon/daemon-resilience.js +279 -0
  51. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  52. package/dist/daemon/daemon-usefulness.js +62 -0
  53. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  54. package/dist/{daemon.js → daemon/daemon.js} +105 -48
  55. package/dist/daemon/heartbeat.d.ts +115 -0
  56. package/dist/daemon/heartbeat.js +141 -0
  57. package/dist/daemon/idle-grace.d.ts +32 -0
  58. package/dist/daemon/idle-grace.js +41 -0
  59. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +23 -2
  60. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
  61. package/dist/daemon/port-presence.d.ts +76 -0
  62. package/dist/daemon/port-presence.js +93 -0
  63. package/dist/daemon/state-dir.d.ts +7 -0
  64. package/dist/daemon/state-dir.js +59 -0
  65. package/dist/daemon/unclean-predecessor.d.ts +23 -0
  66. package/dist/daemon/unclean-predecessor.js +27 -0
  67. package/dist/domain/domain-model.js +2 -2
  68. package/dist/events/body-failures.js +8 -8
  69. package/dist/events/contradictions.d.ts +1 -1
  70. package/dist/events/contradictions.js +60 -10
  71. package/dist/events/echo-mismatch.js +10 -10
  72. package/dist/events/event-filters.js +6 -6
  73. package/dist/events/json-salvage.d.ts +2 -1
  74. package/dist/events/json-salvage.js +6 -6
  75. package/dist/events/observed-in-window.d.ts +12 -0
  76. package/dist/events/observed-in-window.js +21 -0
  77. package/dist/events/predicate-eval.d.ts +53 -20
  78. package/dist/events/predicate-eval.js +179 -55
  79. package/dist/events/predicate-parse.d.ts +23 -0
  80. package/dist/events/predicate-parse.js +54 -0
  81. package/dist/events/predicate.d.ts +7 -2
  82. package/dist/events/predicate.js +126 -35
  83. package/dist/events/reconcile.js +5 -5
  84. package/dist/events/ring-buffer.js +2 -17
  85. package/dist/events/stale-response.js +6 -6
  86. package/dist/events/testid-near-miss.d.ts +22 -0
  87. package/dist/events/testid-near-miss.js +33 -0
  88. package/dist/events/unit-mismatch.js +8 -8
  89. package/dist/flows/affected-tools.js +19 -4
  90. package/dist/flows/affected.js +1 -1
  91. package/dist/flows/annotate-target.d.ts +1 -0
  92. package/dist/flows/annotate-target.js +20 -0
  93. package/dist/flows/annotate-tools.js +25 -3
  94. package/dist/flows/annotate.js +17 -4
  95. package/dist/flows/assertion-integrity.d.ts +2 -1
  96. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  97. package/dist/flows/assertion-tiers-store.js +2 -2
  98. package/dist/flows/attributed-failure.d.ts +17 -0
  99. package/dist/flows/attributed-failure.js +20 -0
  100. package/dist/flows/buddy-status.d.ts +2 -1
  101. package/dist/flows/buddy-status.js +2 -2
  102. package/dist/flows/coverage.d.ts +3 -2
  103. package/dist/flows/coverage.js +2 -2
  104. package/dist/flows/decision.js +24 -9
  105. package/dist/flows/empty-flow.d.ts +6 -0
  106. package/dist/flows/empty-flow.js +24 -0
  107. package/dist/flows/flake-store.d.ts +0 -1
  108. package/dist/flows/flake-store.js +1 -1
  109. package/dist/flows/flake.d.ts +2 -1
  110. package/dist/flows/flake.js +2 -2
  111. package/dist/flows/flow-classify.js +12 -0
  112. package/dist/flows/flow-replay-run.js +4 -4
  113. package/dist/flows/flow-replay.d.ts +6 -8
  114. package/dist/flows/flow-replay.js +132 -48
  115. package/dist/flows/flow-report.js +1 -1
  116. package/dist/flows/flow-scope.d.ts +1 -6
  117. package/dist/flows/flow-step-runners.d.ts +26 -6
  118. package/dist/flows/flow-step-runners.js +156 -6
  119. package/dist/flows/flow-success.js +20 -15
  120. package/dist/flows/flow-tools.js +63 -21
  121. package/dist/flows/flows.js +15 -11
  122. package/dist/flows/gate.d.ts +4 -3
  123. package/dist/flows/gate.js +1 -1
  124. package/dist/flows/heal-run.d.ts +0 -8
  125. package/dist/flows/heal-run.js +5 -5
  126. package/dist/flows/heal.js +3 -3
  127. package/dist/flows/predicate-to-expect.d.ts +38 -0
  128. package/dist/flows/predicate-to-expect.js +95 -0
  129. package/dist/flows/replay.d.ts +16 -1
  130. package/dist/flows/replay.js +58 -22
  131. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  132. package/dist/flows/role-anchor-nearest.js +45 -0
  133. package/dist/flows/role-drift-reason.d.ts +20 -0
  134. package/dist/flows/role-drift-reason.js +39 -0
  135. package/dist/flows/server-verify.d.ts +1 -1
  136. package/dist/flows/server-verify.js +5 -5
  137. package/dist/flows/suite-flakes.d.ts +33 -0
  138. package/dist/flows/suite-flakes.js +32 -0
  139. package/dist/flows/verify-change-tools.js +32 -10
  140. package/dist/flows/verify-change-verdict.d.ts +15 -0
  141. package/dist/flows/verify-change-verdict.js +25 -0
  142. package/dist/flows/watch-batcher.d.ts +3 -2
  143. package/dist/honesty/already-true.d.ts +2 -0
  144. package/dist/honesty/already-true.js +31 -0
  145. package/dist/honesty/blind-spots.d.ts +35 -3
  146. package/dist/honesty/blind-spots.js +57 -12
  147. package/dist/honesty/honesty.d.ts +3 -2
  148. package/dist/honesty/honesty.js +4 -4
  149. package/dist/honesty/uncaptured-bodies.js +1 -1
  150. package/dist/honesty/unread-outcome.js +2 -2
  151. package/dist/honesty/verified.d.ts +31 -3
  152. package/dist/honesty/verified.js +64 -8
  153. package/dist/http-server.js +23 -7
  154. package/dist/hunt/hunt-report.d.ts +2 -1
  155. package/dist/hunt/hunt-report.js +2 -2
  156. package/dist/index.d.ts +9 -6
  157. package/dist/index.js +41 -25
  158. package/dist/init/agent-rules.d.ts +20 -1
  159. package/dist/init/agent-rules.js +64 -8
  160. package/dist/init/app-choice.d.ts +20 -0
  161. package/dist/init/app-choice.js +25 -0
  162. package/dist/init/astro-host.d.ts +21 -0
  163. package/dist/init/astro-host.js +27 -0
  164. package/dist/init/astro-patch.d.ts +16 -0
  165. package/dist/init/astro-patch.js +240 -0
  166. package/dist/init/capabilities.js +4 -1
  167. package/dist/init/cra.d.ts +50 -0
  168. package/dist/init/cra.js +106 -0
  169. package/dist/init/cursor.js +39 -4
  170. package/dist/init/desktop-doctor.d.ts +3 -2
  171. package/dist/init/desktop-doctor.js +4 -4
  172. package/dist/init/detect.d.ts +9 -2
  173. package/dist/init/detect.js +24 -5
  174. package/dist/init/mcp-clients.d.ts +81 -0
  175. package/dist/init/mcp-clients.js +248 -0
  176. package/dist/init/mcp-registered.d.ts +12 -0
  177. package/dist/init/mcp-registered.js +14 -0
  178. package/dist/init/mcp.d.ts +27 -2
  179. package/dist/init/mcp.js +40 -4
  180. package/dist/init/next-patch.d.ts +2 -4
  181. package/dist/init/next-patch.js +109 -19
  182. package/dist/init/node-io.js +29 -6
  183. package/dist/init/plan-framework.d.ts +16 -1
  184. package/dist/init/plan-framework.js +166 -8
  185. package/dist/init/plan.d.ts +85 -6
  186. package/dist/init/plan.js +196 -33
  187. package/dist/init/project-id.d.ts +5 -9
  188. package/dist/init/project-id.js +10 -22
  189. package/dist/init/run.d.ts +23 -1
  190. package/dist/init/run.js +338 -47
  191. package/dist/init/slash-command.d.ts +9 -0
  192. package/dist/init/slash-command.js +10 -1
  193. package/dist/init/snippets.d.ts +7 -10
  194. package/dist/init/snippets.js +44 -15
  195. package/dist/init/vite-config.js +1 -1
  196. package/dist/init/windows-quote.d.ts +38 -0
  197. package/dist/init/windows-quote.js +70 -0
  198. package/dist/init/workspace-apps.d.ts +23 -0
  199. package/dist/init/workspace-apps.js +68 -0
  200. package/dist/input/driven-redaction.js +1 -1
  201. package/dist/input/network-detail.js +8 -8
  202. package/dist/input/network-mock-tools.js +2 -2
  203. package/dist/input/network-mock.js +4 -4
  204. package/dist/input/real-input.js +8 -3
  205. package/dist/input/scroll-find.js +2 -2
  206. package/dist/input/scroll-tools.d.ts +0 -5
  207. package/dist/input/scroll-tools.js +11 -4
  208. package/dist/input/viewport-tools.js +2 -2
  209. package/dist/journal/ambient-store.d.ts +0 -2
  210. package/dist/journal/ambient-store.js +1 -1
  211. package/dist/journal/ambient.js +1 -1
  212. package/dist/journal/attach-journal.d.ts +2 -1
  213. package/dist/journal/deviation-report.js +6 -6
  214. package/dist/journal/deviation-service.js +1 -1
  215. package/dist/journal/envelope-store.d.ts +0 -2
  216. package/dist/journal/envelope-store.js +1 -1
  217. package/dist/journal/envelope.d.ts +2 -3
  218. package/dist/journal/envelope.js +3 -3
  219. package/dist/journal/journal-query.js +1 -1
  220. package/dist/journal/journal-recorder.d.ts +2 -1
  221. package/dist/journal/journal-recorder.js +2 -2
  222. package/dist/journal/rollups.js +9 -9
  223. package/dist/journal/session-end.d.ts +2 -1
  224. package/dist/journal/session-journal.js +4 -4
  225. package/dist/license/license.d.ts +0 -14
  226. package/dist/license/license.js +8 -8
  227. package/dist/log.d.ts +10 -0
  228. package/dist/log.js +11 -1
  229. package/dist/mcp/mcp-is-error.d.ts +13 -0
  230. package/dist/mcp/mcp-is-error.js +18 -0
  231. package/dist/mcp/mcp-outage.d.ts +32 -0
  232. package/dist/mcp/mcp-outage.js +55 -0
  233. package/dist/mcp/mcp-proxy.d.ts +134 -0
  234. package/dist/mcp/mcp-proxy.js +720 -0
  235. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  236. package/dist/{mcp.js → mcp/mcp.js} +139 -37
  237. package/dist/mcp/proxy-handshake.d.ts +3 -0
  238. package/dist/mcp/proxy-handshake.js +54 -0
  239. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  240. package/dist/mcp/proxy-lifecycle.js +70 -0
  241. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  242. package/dist/mcp/tool-catalog-cache.js +55 -0
  243. package/dist/oracles/propose-consequences.d.ts +2 -1
  244. package/dist/oracles/propose-consequences.js +16 -11
  245. package/dist/oracles/self-instrument.d.ts +2 -1
  246. package/dist/oracles/self-instrument.js +1 -1
  247. package/dist/phenomena/phenomena.js +3 -3
  248. package/dist/platform.d.ts +16 -0
  249. package/dist/platform.js +16 -0
  250. package/dist/pool/browser-pool.d.ts +9 -0
  251. package/dist/pool/browser-pool.js +38 -9
  252. package/dist/pool/playwright-launcher.d.ts +15 -2
  253. package/dist/pool/playwright-launcher.js +21 -2
  254. package/dist/project/fs-port.js +1 -1
  255. package/dist/project/project-tools.js +2 -2
  256. package/dist/runs/build-verification-run.js +1 -1
  257. package/dist/runs/repair-prompt.d.ts +0 -6
  258. package/dist/runs/repair-prompt.js +3 -3
  259. package/dist/runs/risk-classify.js +1 -1
  260. package/dist/runs/run-diff.d.ts +0 -2
  261. package/dist/runs/run-diff.js +3 -3
  262. package/dist/runs/run-tools.js +2 -2
  263. package/dist/runs/verification-sync.js +2 -2
  264. package/dist/runs/verify-http.js +1 -1
  265. package/dist/runs/verify-server.js +1 -1
  266. package/dist/session/attachment-history.d.ts +46 -0
  267. package/dist/session/attachment-history.js +65 -0
  268. package/dist/session/command-timeout.d.ts +11 -3
  269. package/dist/session/command-timeout.js +2 -2
  270. package/dist/session/control-envelope.d.ts +6 -0
  271. package/dist/session/control-envelope.js +7 -1
  272. package/dist/session/dev-server-probe.d.ts +13 -0
  273. package/dist/session/dev-server-probe.js +52 -0
  274. package/dist/session/last-act.d.ts +17 -3
  275. package/dist/session/last-act.js +16 -4
  276. package/dist/session/live-control-tools.js +7 -2
  277. package/dist/session/live-control.js +1 -1
  278. package/dist/session/looks-like-dev-server.d.ts +14 -0
  279. package/dist/session/looks-like-dev-server.js +25 -0
  280. package/dist/session/no-session-diagnosis.d.ts +45 -0
  281. package/dist/session/no-session-diagnosis.js +120 -0
  282. package/dist/session/no-session-watch.d.ts +21 -0
  283. package/dist/session/no-session-watch.js +69 -0
  284. package/dist/session/observed-state.d.ts +4 -0
  285. package/dist/session/observed-state.js +19 -1
  286. package/dist/session/output-budget.js +1 -1
  287. package/dist/session/pending-commands.js +1 -0
  288. package/dist/session/session-health.d.ts +8 -1
  289. package/dist/session/session-health.js +8 -7
  290. package/dist/session/session-info.d.ts +43 -8
  291. package/dist/session/session-info.js +46 -1
  292. package/dist/session/session-lease.d.ts +0 -11
  293. package/dist/session/session-lease.js +1 -1
  294. package/dist/session/session-manager.d.ts +6 -0
  295. package/dist/session/session-manager.js +124 -12
  296. package/dist/session/session-readiness.d.ts +1 -1
  297. package/dist/session/session-readiness.js +6 -1
  298. package/dist/session/session-reaper.js +1 -1
  299. package/dist/session/session-reconnect.d.ts +32 -0
  300. package/dist/session/session-reconnect.js +42 -0
  301. package/dist/session/session.d.ts +15 -2
  302. package/dist/session/session.js +77 -26
  303. package/dist/session/state-select.d.ts +1 -1
  304. package/dist/session/state-select.js +1 -1
  305. package/dist/status-payload.d.ts +19 -0
  306. package/dist/status-payload.js +11 -0
  307. package/dist/telemetry/argument-shape.js +10 -1
  308. package/dist/telemetry/browser-mode.d.ts +27 -0
  309. package/dist/telemetry/browser-mode.js +33 -0
  310. package/dist/telemetry/bug-found.d.ts +17 -21
  311. package/dist/telemetry/bug-found.js +120 -8
  312. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  313. package/dist/telemetry/cli-telemetry.js +27 -4
  314. package/dist/telemetry/crash-cause.d.ts +47 -0
  315. package/dist/telemetry/crash-cause.js +104 -0
  316. package/dist/telemetry/daemon-telemetry.d.ts +38 -1
  317. package/dist/telemetry/daemon-telemetry.js +37 -12
  318. package/dist/telemetry/dev-repo.js +4 -4
  319. package/dist/telemetry/error-class.d.ts +26 -0
  320. package/dist/telemetry/error-class.js +52 -0
  321. package/dist/telemetry/error-fingerprint.js +21 -2
  322. package/dist/telemetry/feedback-cli.d.ts +8 -3
  323. package/dist/telemetry/feedback-cli.js +52 -8
  324. package/dist/telemetry/feedback-context.d.ts +23 -6
  325. package/dist/telemetry/feedback-context.js +105 -5
  326. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  327. package/dist/telemetry/feedback-delivery.js +30 -0
  328. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  329. package/dist/telemetry/feedback-outbox.js +87 -0
  330. package/dist/telemetry/feedback.d.ts +22 -1
  331. package/dist/telemetry/feedback.js +67 -8
  332. package/dist/telemetry/git-facts.d.ts +7 -0
  333. package/dist/telemetry/git-facts.js +5 -5
  334. package/dist/telemetry/project-profile.js +2 -1
  335. package/dist/telemetry/session-metrics.d.ts +47 -3
  336. package/dist/telemetry/session-metrics.js +315 -15
  337. package/dist/telemetry/telemetry.d.ts +45 -2
  338. package/dist/telemetry/telemetry.js +150 -37
  339. package/dist/telemetry/verification-of.d.ts +22 -0
  340. package/dist/telemetry/verification-of.js +84 -0
  341. package/dist/tools/act-capsule.js +2 -2
  342. package/dist/tools/act-danger.d.ts +12 -0
  343. package/dist/tools/act-danger.js +20 -3
  344. package/dist/tools/act-helpers.js +4 -4
  345. package/dist/tools/act-tools.d.ts +0 -8
  346. package/dist/tools/act-tools.js +114 -118
  347. package/dist/tools/act-view.js +7 -7
  348. package/dist/tools/alias-args.d.ts +17 -0
  349. package/dist/tools/alias-args.js +25 -0
  350. package/dist/tools/assert-grade.js +9 -9
  351. package/dist/tools/assert-verdict.d.ts +9 -1
  352. package/dist/tools/assert-verdict.js +21 -6
  353. package/dist/tools/browser-tools.js +36 -11
  354. package/dist/tools/contract-tools.d.ts +0 -5
  355. package/dist/tools/contract-tools.js +27 -1
  356. package/dist/tools/coverage-identity.d.ts +34 -0
  357. package/dist/tools/coverage-identity.js +46 -0
  358. package/dist/tools/coverage-tools.js +10 -14
  359. package/dist/tools/dynamic-tools.d.ts +2 -1
  360. package/dist/tools/dynamic-tools.js +96 -19
  361. package/dist/tools/error-recovery.d.ts +34 -2
  362. package/dist/tools/error-recovery.js +255 -4
  363. package/dist/tools/feedback-invite.d.ts +48 -0
  364. package/dist/tools/feedback-invite.js +71 -0
  365. package/dist/tools/feedback-tools.d.ts +12 -1
  366. package/dist/tools/feedback-tools.js +28 -5
  367. package/dist/tools/invoke-tool.d.ts +0 -12
  368. package/dist/tools/invoke-tool.js +126 -33
  369. package/dist/tools/lease-hint.d.ts +17 -0
  370. package/dist/tools/lease-hint.js +24 -0
  371. package/dist/tools/lease-tools.js +6 -5
  372. package/dist/tools/merge-tools.d.ts +10 -1
  373. package/dist/tools/merge-tools.js +9 -3
  374. package/dist/tools/merged-name-redirect.d.ts +13 -0
  375. package/dist/tools/merged-name-redirect.js +55 -0
  376. package/dist/tools/navigate-arrival.d.ts +26 -0
  377. package/dist/tools/navigate-arrival.js +60 -0
  378. package/dist/tools/navigate-result.d.ts +31 -0
  379. package/dist/tools/navigate-result.js +38 -0
  380. package/dist/tools/observe-tools.js +72 -20
  381. package/dist/tools/observed-nothing.d.ts +25 -0
  382. package/dist/tools/observed-nothing.js +33 -0
  383. package/dist/tools/query-paginate.js +2 -2
  384. package/dist/tools/query-shape.d.ts +18 -0
  385. package/dist/tools/query-shape.js +39 -0
  386. package/dist/tools/read-tools.js +81 -20
  387. package/dist/tools/real-input-attempt.d.ts +28 -0
  388. package/dist/tools/real-input-attempt.js +93 -0
  389. package/dist/tools/reconcile-tools.js +7 -7
  390. package/dist/tools/reload-result.d.ts +14 -0
  391. package/dist/tools/reload-result.js +29 -0
  392. package/dist/tools/snapshot-delta.d.ts +4 -5
  393. package/dist/tools/snapshot-delta.js +0 -0
  394. package/dist/tools/tool-kit.d.ts +16 -0
  395. package/dist/tools/tool-surface.d.ts +83 -0
  396. package/dist/tools/tool-surface.js +217 -0
  397. package/dist/tools/tools-helpers.js +6 -6
  398. package/dist/tools/tools.d.ts +15 -0
  399. package/dist/tools/tools.js +53 -17
  400. package/dist/tools/unadvertised-help.d.ts +5 -0
  401. package/dist/tools/unadvertised-help.js +39 -0
  402. package/dist/trace.d.ts +69 -0
  403. package/dist/trace.js +150 -0
  404. package/dist/update/nudge-credit.d.ts +4 -0
  405. package/dist/update/nudge-credit.js +53 -0
  406. package/dist/update/sdk-sync.d.ts +21 -0
  407. package/dist/update/sdk-sync.js +42 -0
  408. package/dist/update/update-checker.js +3 -3
  409. package/dist/update/update-nudge.d.ts +63 -6
  410. package/dist/update/update-nudge.js +128 -15
  411. package/dist/update/updater.d.ts +1 -9
  412. package/dist/update/updater.js +13 -8
  413. package/dist/version/peer-announce.d.ts +4 -0
  414. package/dist/version/peer-announce.js +30 -0
  415. package/dist/{server-version.js → version/server-version.js} +5 -1
  416. package/dist/version/version-nudge.d.ts +28 -0
  417. package/dist/version/version-nudge.js +40 -0
  418. package/dist/version/version-skew.d.ts +66 -0
  419. package/dist/version/version-skew.js +71 -0
  420. package/dist/visual/visual-diff.js +2 -2
  421. package/dist/visual/visual-tools.js +4 -4
  422. package/docs/README.md +55 -0
  423. package/docs/agent-cheatsheet.md +85 -0
  424. package/docs/architecture.md +113 -0
  425. package/docs/benchmarks.md +255 -0
  426. package/docs/debugging.md +102 -0
  427. package/docs/deploy-checks.md +69 -0
  428. package/docs/desktop-apps.md +256 -0
  429. package/docs/enterprise.md +62 -0
  430. package/docs/fixtures-dispatch-receiver.yml +76 -0
  431. package/docs/fixtures.md +45 -0
  432. package/docs/flows.md +166 -0
  433. package/docs/gate-plan.md +98 -0
  434. package/docs/gates.md +117 -0
  435. package/docs/getting-started.md +469 -0
  436. package/docs/human-control.md +63 -0
  437. package/docs/integration-patterns.md +171 -0
  438. package/docs/local-registry.md +126 -0
  439. package/docs/matrix/2.5.0/claude-code-machine.json +19 -0
  440. package/docs/matrix/2.5.0/codex-machine.json +20 -0
  441. package/docs/matrix/2.5.0/cursor-machine.json +22 -0
  442. package/docs/matrix/2.5.0/gemini-machine.json +22 -0
  443. package/docs/matrix/2.5.0/opencode-machine.json +22 -0
  444. package/docs/matrix/2.5.0/vscode-machine.json +22 -0
  445. package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
  446. package/docs/matrix/MATRIX.md +26 -0
  447. package/docs/matrix/README.md +59 -0
  448. package/docs/mint.json +61 -0
  449. package/docs/multi-agent-testing.md +55 -0
  450. package/docs/platform-integration.md +122 -0
  451. package/docs/system-map.md +152 -0
  452. package/docs/telemetry-contract.md +214 -0
  453. package/docs/telemetry.md +191 -0
  454. package/docs/testing.md +82 -0
  455. package/docs/token-efficiency.md +80 -0
  456. package/docs/usage.md +1046 -0
  457. package/package.json +8 -5
  458. package/dist/cli-doctor.d.ts +0 -6
  459. package/dist/cli-doctor.js +0 -60
  460. package/dist/cli-launch.js +0 -112
  461. package/dist/daemon-resilience.d.ts +0 -22
  462. package/dist/daemon-resilience.js +0 -85
  463. package/dist/mcp-proxy.d.ts +0 -67
  464. package/dist/mcp-proxy.js +0 -337
  465. package/dist/tools/profiles.d.ts +0 -37
  466. package/dist/tools/profiles.js +0 -139
  467. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  468. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  469. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  470. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  471. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  472. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -1,9 +1,12 @@
1
- import { ElementState, ReticleCommand, } from '@reticlehq/core';
1
+ import { ElementState, PredicateKind, ReticleCommand, } from '@reticlehq/core';
2
+ import { log } from '../log.js';
3
+ import { bindSpanContext } from '../trace.js';
2
4
  import { selectPath, capDepth } from '../session/state-select.js';
5
+ import { describeTestidMiss } from './testid-near-miss.js';
3
6
  import { predicateToExpectedLinks } from '../capsule/predicate-to-links.js';
4
7
  import { isAmbient, ambientKeyOf } from '../journal/ambient.js';
5
8
  import { PredicateSchema, matchValue, evalNet, evalRoute, evalConsole, evalAnimation, evalSignal, evalSettled, } from './predicate-eval.js';
6
- export { PredicateSchema };
9
+ export { PredicateSchema, PredicateKind };
7
10
  async function matchOnce(session, query, state) {
8
11
  const res = await session.command(ReticleCommand.MATCH, { query, state });
9
12
  if (!res.ok)
@@ -23,7 +26,7 @@ async function evalElement(session, query, state, absent, diagnose) {
23
26
  // silently widening to the whole page is the original false green. So scopeMissing FAILS presence
24
27
  // (on the wait_for path this just keeps polling until the scope appears).
25
28
  if (absent) {
26
- if (match.scopeMissing === true) {
29
+ if (true === match.scopeMissing) {
27
30
  return { pass: true, evidence: { absent: true, scopeMissing: true } };
28
31
  }
29
32
  return match.matched
@@ -37,7 +40,7 @@ async function evalElement(session, query, state, absent, diagnose) {
37
40
  }
38
41
  : { pass: true, evidence: { absent: true } };
39
42
  }
40
- if (match.scopeMissing === true) {
43
+ if (true === match.scopeMissing) {
41
44
  return {
42
45
  pass: false,
43
46
  failureReason: `scope resolved to no element — cannot confirm ${subject} is present`,
@@ -96,12 +99,19 @@ async function evalElement(session, query, state, absent, diagnose) {
96
99
  };
97
100
  }
98
101
  }
102
+ // The testid near-miss: name what IS here, so a typo is one step from fixed rather than a dead
103
+ // end. reticle_query has always done this; the predicate path had no equivalent. See
104
+ // testid-near-miss.ts.
105
+ const present = match.hint?.presentTestids ?? [];
106
+ const alsoHere = query.testid === undefined ? undefined : describeTestidMiss(query.testid, present);
107
+ const suffix = alsoHere === undefined || '' === alsoHere ? '' : ` — ${alsoHere}`;
99
108
  return {
100
109
  pass: false,
101
- failureReason: `no element matched ${subject}${state === undefined ? '' : ` in state '${state}'`}`,
102
- observed: 'no matching element on the page',
110
+ failureReason: `no element matched ${subject}${state === undefined ? '' : ` in state '${state}'`}${suffix}`,
111
+ observed: `no matching element on the page${suffix}`,
103
112
  expected: `an element matching ${subject}${state === undefined ? '' : ` in state '${state}'`}`,
104
113
  assertion: 'element.present',
114
+ ...(present.length > 0 ? { evidence: { presentTestids: present } } : {}),
105
115
  };
106
116
  }
107
117
  async function evalState(session, p) {
@@ -117,14 +127,15 @@ async function evalState(session, p) {
117
127
  }
118
128
  const stores = (res.result ?? {}).stores ?? {};
119
129
  const names = Object.keys(stores);
120
- const storeName = p.store ?? (names.length === 1 ? names[0] : undefined);
130
+ const storeName = p.store ?? (1 === names.length ? names[0] : undefined);
121
131
  if (storeName === undefined) {
122
- return {
123
- pass: false,
124
- failureReason: names.length === 0
125
- ? 'no registered store to read state from'
126
- : `multiple stores (${names.join(', ')}); name one with \`store\``,
127
- };
132
+ // Neither of these is a finding about the app. With several stores registered the call simply did
133
+ // not say which one to read; with none, there is nothing to read at all. No assertion was
134
+ // evaluated either way, so this is inconclusive rather than failed — see honesty/inconclusive.
135
+ const reason = 0 === names.length
136
+ ? 'no registered store to read state from'
137
+ : `multiple stores (${names.join(', ')}); name one with \`store\``;
138
+ return { pass: false, failureReason: reason, inconclusive: reason };
128
139
  }
129
140
  const selection = selectPath(stores[storeName], p.path);
130
141
  if (!selection.found) {
@@ -153,31 +164,39 @@ async function evalState(session, p) {
153
164
  evidence: { store: storeName, path: p.path, value: capDepth(selection.value, 1) },
154
165
  };
155
166
  }
167
+ /** The predicate's own event-time floor, if its kind carries one. */
168
+ function predicateSince(predicate) {
169
+ return 'since' in predicate && 'number' === typeof predicate.since ? predicate.since : 0;
170
+ }
156
171
  export async function evaluatePredicate(session, predicate, since = 0,
157
172
  // Compute the (extra-round-trip) near-miss diagnostics on element failures. Default true so a
158
173
  // one-shot assert is fully diagnostic; the wait loop passes false on its interim polls (which read
159
174
  // only `pass`) and true on the final timeout eval, so a flood no longer pays for a diagnostic nobody
160
175
  // reads. Only element/text failures have a near-miss; everything else ignores this.
161
176
  diagnose = true) {
162
- const events = session.eventsSince(since);
177
+ // A predicate's own `since` is a TIGHTER event-time floor than the caller's — an agent that took
178
+ // `since` from the act it just performed is scoping the assertion to that action's aftermath. It is
179
+ // applied here, once, rather than in each eval: the floor means the same thing for every kind that
180
+ // reads the event stream, and net/console re-applying it is a no-op.
181
+ const events = session.eventsSince(Math.max(since, predicateSince(predicate)));
163
182
  switch (predicate.kind) {
164
- case 'element':
183
+ case PredicateKind.ELEMENT:
165
184
  return evalElement(session, predicate.query, predicate.state, predicate.absent ?? false, diagnose);
166
- case 'text':
167
- return evalElement(session, { text: predicate.contains }, predicate.visible === true ? ElementState.VISIBLE : undefined, predicate.absent ?? false, diagnose);
168
- case 'net':
185
+ case PredicateKind.TEXT:
186
+ return evalElement(session, { text: predicate.contains }, true === predicate.visible ? ElementState.VISIBLE : undefined, predicate.absent ?? false, diagnose);
187
+ case PredicateKind.NET:
169
188
  return evalNet(events, predicate);
170
- case 'route':
189
+ case PredicateKind.ROUTE:
171
190
  return evalRoute(events, predicate);
172
- case 'console':
191
+ case PredicateKind.CONSOLE:
173
192
  return evalConsole(events, predicate);
174
- case 'animation':
193
+ case PredicateKind.ANIMATION:
175
194
  return evalAnimation(events, predicate);
176
- case 'signal':
195
+ case PredicateKind.SIGNAL:
177
196
  return evalSignal(events, predicate);
178
- case 'state':
197
+ case PredicateKind.STATE:
179
198
  return evalState(session, predicate);
180
- case 'settled': {
199
+ case PredicateKind.SETTLED: {
181
200
  // Drop events on learned-ambient regions (chat/ticker churn) before the settle check — by ref
182
201
  // alone, NOT by attribution: window-attribution ("happened during the action window") is a time
183
202
  // heuristic, never causation, so a chat message arriving mid-window must not hold settle open.
@@ -185,7 +204,7 @@ diagnose = true) {
185
204
  const settleEvents = counts === undefined ? events : events.filter((e) => !isAmbient(counts, ambientKeyOf(e)));
186
205
  return evalSettled(settleEvents, predicate, session.elapsed());
187
206
  }
188
- case 'allOf': {
207
+ case PredicateKind.ALL_OF: {
189
208
  const results = await Promise.all(predicate.predicates.map((p) => evaluatePredicate(session, p, since, diagnose)));
190
209
  const failed = results.find((r) => !r.pass);
191
210
  return failed === undefined
@@ -196,14 +215,14 @@ diagnose = true) {
196
215
  evidence: results,
197
216
  };
198
217
  }
199
- case 'anyOf': {
218
+ case PredicateKind.ANY_OF: {
200
219
  const results = await Promise.all(predicate.predicates.map((p) => evaluatePredicate(session, p, since, diagnose)));
201
220
  const passed = results.find((r) => r.pass);
202
221
  return passed !== undefined
203
222
  ? { pass: true, evidence: passed.evidence }
204
223
  : { pass: false, failureReason: 'no sub-predicate of anyOf matched', evidence: results };
205
224
  }
206
- case 'not': {
225
+ case PredicateKind.NOT: {
207
226
  const inner = await evaluatePredicate(session, predicate.predicate, since, diagnose);
208
227
  return inner.pass
209
228
  ? { pass: false, failureReason: 'negated predicate unexpectedly held', evidence: inner }
@@ -244,12 +263,12 @@ const COUNT_CONFIRM_MS = 300;
244
263
  * must stay that way, or every ordinary wait pays the confirmation delay for nothing.
245
264
  */
246
265
  function assertsExactCount(predicate) {
247
- if (predicate.kind === 'allOf' || predicate.kind === 'anyOf') {
266
+ if (PredicateKind.ALL_OF === predicate.kind || PredicateKind.ANY_OF === predicate.kind) {
248
267
  return predicate.predicates.some(assertsExactCount);
249
268
  }
250
- if (predicate.kind === 'not')
269
+ if (PredicateKind.NOT === predicate.kind)
251
270
  return assertsExactCount(predicate.predicate);
252
- return predicate.kind === 'net' && predicate.count !== undefined;
271
+ return PredicateKind.NET === predicate.kind && predicate.count !== undefined;
253
272
  }
254
273
  /**
255
274
  * Evaluate now, else wait for it to become true (on each event + a poll) until timeout. `since` is
@@ -263,19 +282,36 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
263
282
  failureReason: error instanceof Error ? error.message : String(error),
264
283
  });
265
284
  let cooldownTimer;
285
+ /** One-shot re-check timed to when a time-based predicate could first pass. See retryAfterMs. */
286
+ let hintTimer;
266
287
  // An exact-count wait keeps watching after it first reads true — see COUNT_CONFIRM_MS.
267
288
  const holdsForCount = assertsExactCount(predicate);
268
289
  let confirming = false;
269
290
  let confirmTimer;
291
+ /** Report a wait that could not run, and END it — see guardedCheck. */
292
+ const failWait = (error) => {
293
+ log('reticle_wait_failed', {
294
+ predicate: predicate.kind,
295
+ error: error instanceof Error ? error.message : String(error),
296
+ });
297
+ finish({
298
+ ...failed(error),
299
+ failureReason: 'the wait could not be evaluated and was ended rather than left pending: ' +
300
+ (error instanceof Error ? error.message : String(error)),
301
+ });
302
+ };
270
303
  const finish = (result) => {
271
304
  if (done)
272
305
  return;
273
306
  done = true;
274
307
  unsub();
308
+ unsubDisconnect?.();
275
309
  clearInterval(interval);
276
310
  clearTimeout(timer);
277
311
  if (cooldownTimer !== undefined)
278
312
  clearTimeout(cooldownTimer);
313
+ if (hintTimer !== undefined)
314
+ clearTimeout(hintTimer);
279
315
  if (confirmTimer !== undefined)
280
316
  clearTimeout(confirmTimer);
281
317
  resolve(result);
@@ -305,8 +341,19 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
305
341
  // final timeout eval below runs with full diagnostics.
306
342
  void evaluatePredicate(session, predicate, since, false)
307
343
  .then((r) => {
308
- if (!r.pass)
344
+ if (!r.pass) {
345
+ // A time-based failure knows when it could stop being one — re-check THEN rather than on
346
+ // the next blind tick. Without this, every `settled` wait paid up to a full poll interval
347
+ // of dead time after the quiet window had already closed: measured at 566–627ms across
348
+ // the fleet for a 500ms window, on the call an agent makes after almost every action.
349
+ // Additive — the backstop interval below still runs, so a missed hint costs nothing.
350
+ const hint = r.retryAfterMs;
351
+ if ('number' === typeof hint && hint > 0 && hint < POLL_INTERVAL_MS) {
352
+ clearTimeout(hintTimer);
353
+ hintTimer = setTimeout(check, hint);
354
+ }
309
355
  return;
356
+ }
310
357
  // "Exactly N" cannot be concluded from a passing sample — the count can still rise. Hold,
311
358
  // re-evaluate WITH diagnostics, and let that second read be the verdict: if an N+1th
312
359
  // arrived in the meantime it now fails, carrying observed/expected rather than a bare no.
@@ -344,10 +391,54 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
344
391
  }, MIN_RECHECK_GAP_MS);
345
392
  });
346
393
  };
394
+ /**
395
+ * Run a re-check so that NOTHING can leave this promise pending.
396
+ *
397
+ * `check` is fired from an event listener and an interval, neither of which is inside the
398
+ * awaited chain. A synchronous throw there reached the process as an uncaughtException; a
399
+ * rejection reached it as an unhandledRejection. Either way the wait never resolved and the tool
400
+ * handler never returned — the agent sees a call that simply never comes back.
401
+ *
402
+ * This half covers the SYNCHRONOUS throw only. The rejection half is handled inside `check`, by
403
+ * the `.catch` on its own promise chain — `check` returns void and voids the promise it starts,
404
+ * so there is nothing to hand back here to await. Said explicitly because the obvious-looking
405
+ * `if (result instanceof Promise) result.catch(…)` that used to sit here was dead code that read
406
+ * like the rejection guard, and removing `check`'s own `.catch` as redundant would have restored
407
+ * the exact hang this exists to prevent.
408
+ *
409
+ * That is the exact shape reported from a Plane (Next 14 + MobX) session, which tears the page
410
+ * session down and rebuilds it on EVERY navigation, so a `{kind:"route"}` predicate always races
411
+ * a teardown of the very session it is watching: `browser.command ok:true` for the click, and
412
+ * then no `tool.handler` for that callId, ever.
413
+ *
414
+ * A wait that cannot evaluate is a FAILED wait, not an eternal one. It now says so and finishes.
415
+ */
416
+ const guardedCheck = () => {
417
+ try {
418
+ check();
419
+ }
420
+ catch (error) {
421
+ failWait(error);
422
+ }
423
+ };
424
+ // Bound to the CALL that started this wait. The listener fires from the WebSocket message
425
+ // handler and the interval from the timer queue — neither is in the awaited chain, so without
426
+ // this every re-check opened a new call at depth 0 and emitted a `browser.command` with no
427
+ // `tool.handler`. Measured on one healthy run: 23 such orphans, which is precisely the
428
+ // documented signature of a HUNG call. A diagnostic that fires on healthy runs is not one.
429
+ const boundCheck = bindSpanContext(guardedCheck);
347
430
  const unsub = session.onEvent(() => {
348
- check();
431
+ boundCheck();
432
+ });
433
+ const unsubDisconnect = session.onDisconnect?.(() => {
434
+ // `observationLost` is what stops this being graded as an app defect. `pass: false` is still
435
+ // correct — the consequence was not seen to hold — but on its own it reached the verdict rule
436
+ // as ASSERTION_FAILED, so a reload mid-wait reported "the declared consequence did not hold"
437
+ // against a healthy component, by file and line. The flag is structured rather than inferred
438
+ // from this string, because every other `failureReason` here is prose about the APP.
439
+ finish({ pass: false, failureReason: 'session disconnected', observationLost: true });
349
440
  });
350
- const interval = setInterval(check, POLL_INTERVAL_MS);
441
+ const interval = setInterval(boundCheck, POLL_INTERVAL_MS);
351
442
  const timer = setTimeout(() => {
352
443
  void evaluatePredicate(session, predicate, since)
353
444
  .then((r) => {
@@ -382,11 +473,11 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
382
473
  * green ⇏ this-branch-held, re-checks each branch and keeps the winners.
383
474
  */
384
475
  export async function provenExpectedLinks(session, predicate, since = 0) {
385
- if (predicate.kind === 'allOf') {
476
+ if (PredicateKind.ALL_OF === predicate.kind) {
386
477
  const per = await Promise.all(predicate.predicates.map((p) => provenExpectedLinks(session, p, since)));
387
478
  return per.flat();
388
479
  }
389
- if (predicate.kind === 'anyOf') {
480
+ if (PredicateKind.ANY_OF === predicate.kind) {
390
481
  const per = await Promise.all(predicate.predicates.map(async (p) => (await evaluatePredicate(session, p, since)).pass
391
482
  ? provenExpectedLinks(session, p, since)
392
483
  : []));
@@ -39,7 +39,7 @@ const ZERO_DECIMAL = new Set(['JPY', 'KRW', 'VND']);
39
39
  const STATE_FIELDS = new Set(['status', 'state']);
40
40
  const ITEM_KEYS = ['items', 'results', 'data', 'records', 'rows', 'entries'];
41
41
  function isRecord(value) {
42
- return typeof value === 'object' && value !== null && !Array.isArray(value);
42
+ return 'object' === typeof value && value !== null && !Array.isArray(value);
43
43
  }
44
44
  /** Every object carrying an `id`, at any depth. These are the things a UI renders as rows. */
45
45
  export function entitiesIn(node, out = []) {
@@ -102,7 +102,7 @@ function wrongCurrencyMark(text, amounts, expected) {
102
102
  }
103
103
  function markBefore(text, amount, expected) {
104
104
  const at = text.indexOf(amount);
105
- if (at === -1)
105
+ if (-1 === at)
106
106
  return undefined; // this rendering is not on screen — try the next
107
107
  // Look just before the number, where a symbol or code sits. A window, not the whole page: the
108
108
  // point is what is attached to THIS amount.
@@ -167,9 +167,9 @@ export function reconcile(bodies, pageText) {
167
167
  // number must appear with a currency marker attached. A non-money number that happens to share a
168
168
  // decimal form (a weight, a count) is rendered bare and never reaches the comparison.
169
169
  const currency = entity['currency'];
170
- if (typeof currency === 'string') {
170
+ if ('string' === typeof currency) {
171
171
  for (const [field, value] of Object.entries(entity)) {
172
- if (typeof value !== 'number' || field === 'currency')
172
+ if (typeof value !== 'number' || 'currency' === field)
173
173
  continue;
174
174
  const printed = majorForms(value, currency);
175
175
  const wrong = wrongCurrencyMark(pageText, printed, currency);
@@ -196,7 +196,7 @@ export function reconcile(bodies, pageText) {
196
196
  const others = [...(vocabulary.get(field) ?? [])].filter((v) => v !== value);
197
197
  const shown = others.filter((v) => shows(pageText, v));
198
198
  const key = `${id}:${field}`;
199
- if (shown.length === 0 || seen.has(key))
199
+ if (0 === shown.length || seen.has(key))
200
200
  continue;
201
201
  seen.add(key);
202
202
  found.push({
@@ -1,19 +1,4 @@
1
- import { EventType, RING_BUFFER_DEFAULTS } from '@reticlehq/core';
2
- /**
3
- * The high-volume, low-signal floor. These fire continuously on any live page (count-up text, CSS
4
- * animation frames, React commits, health heartbeats, scroll) and are the ONLY thing that should be
5
- * sacrificed when the buffer is full. Everything else — a failed request, a console error, a domain
6
- * signal, a truncation/blind-spot marker — is scarce evidence a verdict depends on, and a churning
7
- * region must never be able to push it out of the window.
8
- */
9
- const CHURN_TYPES = new Set([
10
- EventType.DOM_TEXT,
11
- EventType.ANIM_START,
12
- EventType.ANIM_END,
13
- EventType.RENDER_COMMIT,
14
- EventType.PAGE_HEALTH,
15
- EventType.SCROLL_POSITION,
16
- ]);
1
+ import { CHURN_TYPES, RING_BUFFER_DEFAULTS } from '@reticlehq/core';
17
2
  /** How far forward to look for a churn event to sacrifice before falling back to plain FIFO. */
18
3
  const CHURN_SCAN_LIMIT = 256;
19
4
  /**
@@ -95,7 +80,7 @@ export class RingBuffer {
95
80
  continue;
96
81
  }
97
82
  const victim = this.#findChurnAfterHead();
98
- if (victim === -1) {
83
+ if (-1 === victim) {
99
84
  // Buffer genuinely full of high-signal events — fall back to FIFO so it stays bounded.
100
85
  this.#totalBytes -= this.#eventBytes[this.#head] ?? 0;
101
86
  this.#head += 1;
@@ -1,11 +1,11 @@
1
1
  import { ContradictionKind, EventType } from '@reticlehq/core';
2
2
  function pathOf(url) {
3
3
  const q = url.indexOf('?');
4
- return q === -1 ? url : url.slice(0, q);
4
+ return -1 === q ? url : url.slice(0, q);
5
5
  }
6
6
  function queryOf(url) {
7
7
  const q = url.indexOf('?');
8
- return q === -1 ? '' : url.slice(q + 1);
8
+ return -1 === q ? '' : url.slice(q + 1);
9
9
  }
10
10
  /**
11
11
  * Parameters that ENUMERATE a collection rather than select within it.
@@ -52,13 +52,13 @@ function flights(events) {
52
52
  const issued = new Map();
53
53
  const out = [];
54
54
  for (const event of events) {
55
- const id = typeof event.data['id'] === 'string' ? event.data['id'] : undefined;
55
+ const id = 'string' === typeof event.data['id'] ? event.data['id'] : undefined;
56
56
  if (id === undefined)
57
57
  continue;
58
- const at = typeof event.t === 'number' ? event.t : 0;
58
+ const at = 'number' === typeof event.t ? event.t : 0;
59
59
  if (event.type === EventType.NET_PENDING) {
60
- const url = typeof event.data['url'] === 'string' ? event.data['url'] : '';
61
- const method = typeof event.data['method'] === 'string' ? event.data['method'] : 'GET';
60
+ const url = 'string' === typeof event.data['url'] ? event.data['url'] : '';
61
+ const method = 'string' === typeof event.data['method'] ? event.data['method'] : 'GET';
62
62
  issued.set(id, { url, method, at });
63
63
  continue;
64
64
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Turning "no element matched {testid: …}" into something an agent can act on.
3
+ *
4
+ * `reticle_query` on a missing testid already returns the testids that ARE present, so a typo is one
5
+ * step from fixed. The same failure through a PREDICATE — `act_and_wait { until: { kind:'element',
6
+ * query:{ testid } } }` — said only "no element matched" and stopped, naming nothing.
7
+ *
8
+ * The near-miss machinery covered an element in the wrong STATE and a wrong NAME for a role (which
9
+ * even lists the names it saw). Testid — the anchor this codebase calls the gold standard — had
10
+ * none, and it is the one agents use most.
11
+ *
12
+ * It also just became the highest-traffic failure in the product: putting act_and_wait on the
13
+ * verification path means agents assert far more, so a failed element assertion is now the common
14
+ * case rather than the rare one.
15
+ */
16
+ /**
17
+ * The "…but these ARE here" clause, or undefined when there is nothing useful to say.
18
+ *
19
+ * Returns the empty string when the queried testid is itself present: the miss was then about STATE,
20
+ * not presence, and "you asked for X; X is present" reads as a contradiction rather than a hint.
21
+ */
22
+ export declare function describeTestidMiss(wanted: string, present: readonly string[]): string | undefined;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Turning "no element matched {testid: …}" into something an agent can act on.
3
+ *
4
+ * `reticle_query` on a missing testid already returns the testids that ARE present, so a typo is one
5
+ * step from fixed. The same failure through a PREDICATE — `act_and_wait { until: { kind:'element',
6
+ * query:{ testid } } }` — said only "no element matched" and stopped, naming nothing.
7
+ *
8
+ * The near-miss machinery covered an element in the wrong STATE and a wrong NAME for a role (which
9
+ * even lists the names it saw). Testid — the anchor this codebase calls the gold standard — had
10
+ * none, and it is the one agents use most.
11
+ *
12
+ * It also just became the highest-traffic failure in the product: putting act_and_wait on the
13
+ * verification path means agents assert far more, so a failed element assertion is now the common
14
+ * case rather than the rare one.
15
+ */
16
+ /** Enough to recognise the one you meant; not so many that a large page floods the message. */
17
+ const MAX_LISTED = 12;
18
+ /**
19
+ * The "…but these ARE here" clause, or undefined when there is nothing useful to say.
20
+ *
21
+ * Returns the empty string when the queried testid is itself present: the miss was then about STATE,
22
+ * not presence, and "you asked for X; X is present" reads as a contradiction rather than a hint.
23
+ */
24
+ export function describeTestidMiss(wanted, present) {
25
+ if (0 === present.length)
26
+ return undefined;
27
+ if (present.includes(wanted))
28
+ return '';
29
+ const shown = present.slice(0, MAX_LISTED);
30
+ const rest = present.length - shown.length;
31
+ const tail = rest > 0 ? `, and ${String(rest)} more` : '';
32
+ return `testids present here: ${shown.join(', ')}${tail}`;
33
+ }
@@ -37,7 +37,7 @@ const SCALES = [100, 1000];
37
37
  */
38
38
  const ID_PATTERN = /[A-Za-z]{2,}_[A-Za-z0-9]{2,}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}/g;
39
39
  function parse(raw) {
40
- if (typeof raw !== 'string' || raw.length === 0)
40
+ if (typeof raw !== 'string' || 0 === raw.length)
41
41
  return undefined;
42
42
  try {
43
43
  return JSON.parse(raw);
@@ -53,12 +53,12 @@ function moneyByEntity(node, into) {
53
53
  moneyByEntity(item, into);
54
54
  return;
55
55
  }
56
- if (typeof node !== 'object' || node === null)
56
+ if (typeof node !== 'object' || null === node)
57
57
  return;
58
58
  const record = node;
59
59
  // An id may arrive as a number (`{"id": 9}`) — normalised so a numeric path segment matches it.
60
60
  const raw = record['id'];
61
- const id = typeof raw === 'string' ? raw : typeof raw === 'number' ? String(raw) : undefined;
61
+ const id = 'string' === typeof raw ? raw : 'number' === typeof raw ? String(raw) : undefined;
62
62
  if (id !== undefined) {
63
63
  for (const [key, value] of Object.entries(record)) {
64
64
  if (!MONEY_FIELDS.has(key) || typeof value !== 'number')
@@ -79,9 +79,9 @@ function idsIn(url, body) {
79
79
  if (segment.length > 0 && /^\d+$/.test(segment))
80
80
  found.add(segment);
81
81
  }
82
- if (typeof body === 'object' && body !== null) {
82
+ if ('object' === typeof body && body !== null) {
83
83
  for (const value of Object.values(body)) {
84
- if (typeof value === 'string')
84
+ if ('string' === typeof value)
85
85
  for (const id of value.match(ID_PATTERN) ?? [])
86
86
  found.add(id);
87
87
  }
@@ -90,7 +90,7 @@ function idsIn(url, body) {
90
90
  }
91
91
  /** The scale between a known value and a sent one, or undefined when they are consistent. */
92
92
  function scaleBetween(known, sent) {
93
- if (sent === 0 || known === 0)
93
+ if (0 === sent || 0 === known)
94
94
  return undefined;
95
95
  for (const scale of SCALES) {
96
96
  // A tolerance, because the major-unit form is a rounded render of the minor one.
@@ -127,11 +127,11 @@ export function findUnitMismatches(events, prior = []) {
127
127
  for (const event of events) {
128
128
  if (event.type !== EventType.NET_REQUEST)
129
129
  continue;
130
- const url = typeof event.data['url'] === 'string' ? event.data['url'] : '';
130
+ const url = 'string' === typeof event.data['url'] ? event.data['url'] : '';
131
131
  const requestBody = parse(event.data['requestBody']);
132
132
  // Compare BEFORE recording this call's own response, or a request that echoes its own reply
133
133
  // would compare against itself.
134
- if (typeof requestBody === 'object' && requestBody !== null) {
134
+ if ('object' === typeof requestBody && requestBody !== null) {
135
135
  const sentFields = requestBody;
136
136
  for (const id of idsIn(url, requestBody)) {
137
137
  const fields = known.get(id);
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { ReticleTool } from '../tools/tool-names.js';
3
- import { loadNamedFlows, resolveChangedFiles } from '../cli-flow-commands.js';
3
+ import { loadNamedFlows, resolveChangedFiles } from '../cli/cli-flow-commands.js';
4
4
  import { affectedSavedFlows } from './flow-sources.js';
5
5
  /**
6
6
  * `reticle_affected` — which saved flows must re-verify for a set of changed files.
@@ -40,13 +40,28 @@ export const AFFECTED_TOOLS = [
40
40
  },
41
41
  handler: async (deps, args) => {
42
42
  const files = Array.isArray(args['files'])
43
- ? args['files'].filter((f) => typeof f === 'string')
43
+ ? args['files'].filter((f) => 'string' === typeof f)
44
44
  : [];
45
- const since = typeof args['since'] === 'string' ? args['since'] : undefined;
45
+ const since = 'string' === typeof args['since'] ? args['since'] : undefined;
46
46
  const changedFiles = await resolveChangedFiles(files, since);
47
47
  const flows = await loadNamedFlows(deps.fs, deps.reticleRoot);
48
48
  const result = affectedSavedFlows(flows, changedFiles);
49
- return { changedFiles, ...result };
49
+ // Three different situations produced the same empty answer: no files changed, no flows saved,
50
+ // and a git ref that resolved to nothing. Only the first means "nothing to re-verify"; the
51
+ // others mean the question could not be answered, and re-running nothing on that basis is how a
52
+ // regression ships. Say which one it is.
53
+ const why = 0 === flows.length
54
+ ? 'no saved flows exist yet, so nothing COULD be affected — record one with reticle_record then reticle_flow_save'
55
+ : 0 === changedFiles.length
56
+ ? since === undefined && 0 === files.length
57
+ ? 'no files were given and no `since` ref was passed, so nothing was compared — pass the files you edited, or a git ref'
58
+ : 'nothing changed against that input, so no saved flow needs re-verifying'
59
+ : undefined;
60
+ return {
61
+ changedFiles,
62
+ ...result,
63
+ ...(why === undefined ? {} : { observed: true, note: why }),
64
+ };
50
65
  },
51
66
  },
52
67
  ];
@@ -23,7 +23,7 @@ export function affectedFlows(flows, changedFiles) {
23
23
  const affected = [];
24
24
  const unknownProvenance = [];
25
25
  for (const flow of flows) {
26
- if (flow.sources === undefined || flow.sources.length === 0) {
26
+ if (flow.sources === undefined || 0 === flow.sources.length) {
27
27
  affected.push(flow.name);
28
28
  unknownProvenance.push(flow.name);
29
29
  continue;
@@ -0,0 +1 @@
1
+ export declare function resolveAnnotateTarget(explicit: string | undefined, active: readonly string[]): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Which recording an annotation belongs to when the call does not say.
3
+ *
4
+ * It used to be the literal name `default`, unconditionally. Reproduced on three apps: an agent that
5
+ * starts `reticle_record { recordingName: "my-flow" }`, acts, and then annotates without repeating
6
+ * the name gets `annotate_no_step` — because the steps are in "my-flow" and the annotation went
7
+ * looking in an empty "default". The agent's rational next move is to record MORE steps, into the
8
+ * wrong place.
9
+ *
10
+ * With exactly one recording in progress there is nothing to disambiguate, so that is the answer.
11
+ * With several, `default` stays the documented choice: silently picking one of the others would put
12
+ * an assertion in a flow the agent never named, which is worse than an error.
13
+ */
14
+ /** The documented fallback name, matching what `reticle_record` starts when given none. */
15
+ const DEFAULT_RECORDING = 'default';
16
+ export function resolveAnnotateTarget(explicit, active) {
17
+ if (explicit !== undefined && explicit.length > 0)
18
+ return explicit;
19
+ return 1 === active.length ? (active[0] ?? DEFAULT_RECORDING) : DEFAULT_RECORDING;
20
+ }