@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
@@ -2,12 +2,15 @@
2
2
  * Observe / wait / assert tools — reticle_observe, reticle_wait_for, reticle_assert, reticle_network,
3
3
  * reticle_console, reticle_animations. Split out of tools.ts; assembled back via...OBSERVE_TOOLS.
4
4
  */
5
+ import { noteEmptyRead } from './observed-nothing.js';
5
6
  import { z } from 'zod';
6
- import { ReticleCommand, DEFAULT_ASSERT_TIMEOUT_MS } from '@reticlehq/core';
7
+ import { aliasParam } from './alias-args.js';
8
+ import { CONSOLE_LEVELS, ReticleCommand, DEFAULT_ASSERT_TIMEOUT_MS, PredicateKind, } from '@reticlehq/core';
7
9
  import { ReticleTool } from './tool-names.js';
8
10
  import { buildReactionReport } from '../events/reaction.js';
9
11
  import { findContradictions } from '../events/contradictions.js';
10
12
  import { evaluatePredicate, waitForPredicate, PredicateSchema } from '../events/predicate.js';
13
+ import { parsePredicate } from '../events/predicate-parse.js';
11
14
  import { matchNet, matchConsole, isConsoleEvent, eventMatchesFilters, netEmptyHint, consoleEmptyHint, reconcileNet, projectNetCall, projectConsoleLog, } from '../events/event-filters.js';
12
15
  import { applyEventBudget, costHint, withSizeCost, DEFAULT_QUERY_LIMIT, } from '../session/output-budget.js';
13
16
  import { healthEnvelope, bufferEnvelope } from '../session/session-health.js';
@@ -54,11 +57,21 @@ function lastActSourceOnFailure(session, pass) {
54
57
  * really do interleave; it is only redundant at this boundary.
55
58
  */
56
59
  function withoutConstantSessionId(event) {
57
- if (typeof event !== 'object' || event === null)
60
+ if (typeof event !== 'object' || null === event)
58
61
  return event;
59
62
  const { sessionId: _sessionId, ...rest } = event;
60
63
  return rest;
61
64
  }
65
+ /**
66
+ * The console levels, derived from core's console EventTypes — see CONSOLE_LEVELS.
67
+ *
68
+ * A free string here filtered by building `console.${level}`, so `level:'ERROR'` or `level:'fatal'`
69
+ * matched nothing and returned an empty log list. On a page that HAS logs the zero-match hint saves
70
+ * it (it reports which levels are present), but on a quiet page the answer is identical to a genuine
71
+ * all-clear. Refusing the value outright is the only reading with no ambiguity.
72
+ */
73
+ const CONSOLE_LEVEL_LIST = CONSOLE_LEVELS.join(' | ');
74
+ const consoleLevelEnum = z.enum(CONSOLE_LEVELS);
62
75
  export const OBSERVE_TOOLS = [
63
76
  {
64
77
  name: ReticleTool.OBSERVE,
@@ -180,10 +193,12 @@ export const OBSERVE_TOOLS = [
180
193
  },
181
194
  {
182
195
  name: ReticleTool.WAIT_FOR,
183
- example: { predicate: { kind: 'state', path: 'todos.length', equals: 3 } },
196
+ example: { predicate: { kind: PredicateKind.STATE, path: 'todos.length', equals: 3 } },
184
197
  description: 'Block until a predicate is satisfied (or already true in the recent buffer), else time out. Returns matching evidence or a near-miss diagnosis. By default it only counts events since your last act, so a signal buffered BEFORE the action can never fake a pass; pass `since` (an observe/act cursor) to widen or narrow that window explicitly.',
185
198
  inputSchema: {
186
- predicate: PredicateSchema.describe('Predicate to wait for: { signal }, { net }, { element }, { kind: "net", ok: false } (assert on the OUTCOME — the honest field for IPC, which has no status code), { kind: "state", store, path, equals } (assert a registered store\'s value directly — the source of truth no DOM read can reach; equals takes a literal or { $gte | $contains | $length } pattern), { kind: "settled", quietMs } (deterministic network + DOM idle — prefer this over a fixed sleep), or a combination via allOf/anyOf.'),
199
+ predicate: PredicateSchema.optional().describe('Predicate to wait for: { signal }, { net }, { element }, { kind: "net", ok: false } (assert on the OUTCOME — the honest field for IPC, which has no status code), { kind: "state", store, path, equals } (assert a registered store\'s value directly — the source of truth no DOM read can reach; equals takes a literal or { $gte | $contains | $length } pattern), { kind: "settled", quietMs } (deterministic network + DOM idle — prefer this over a fixed sleep), or a combination via allOf/anyOf.'),
200
+ // Same concept, the neighbouring tool's name. See alias-args.ts.
201
+ until: PredicateSchema.optional().describe("Alias for `predicate` (act_and_wait's name)."),
187
202
  timeout_ms: z.number().optional().describe('Maximum wait in milliseconds. Default: 4000.'),
188
203
  since: z
189
204
  .number()
@@ -195,6 +210,10 @@ export const OBSERVE_TOOLS = [
195
210
  pass: z.boolean(),
196
211
  evidence: z.unknown().optional(),
197
212
  failureReason: z.string().optional(),
213
+ observationLost: z
214
+ .boolean()
215
+ .optional()
216
+ .describe('The tab disconnected mid-wait, so this was never observed — the verdict is UNKNOWN, not a failure of the app.'),
198
217
  session: z
199
218
  .object({ lastSeenMs: z.number(), throttled: z.boolean(), focused: z.boolean() })
200
219
  .optional(),
@@ -218,7 +237,8 @@ export const OBSERVE_TOOLS = [
218
237
  },
219
238
  handler: async (deps, args) => {
220
239
  const session = deps.sessions.resolve(asString(args['sessionId']));
221
- const predicate = PredicateSchema.parse(args['predicate']);
240
+ // `until` is act_and_wait's name for this — see alias-args.ts.
241
+ const predicate = parsePredicate(aliasParam(args, 'predicate', ['until'])['predicate']);
222
242
  // Honesty: explicit since wins; else default to the last act's cursor; else the whole buffer.
223
243
  const since = asNumber(args['since']) ?? session.lastAct.cursor() ?? 0;
224
244
  const verdict = await waitForPredicate(session, predicate, asNumber(args['timeout_ms']) ?? DEFAULT_ASSERT_TIMEOUT_MS, since);
@@ -234,10 +254,20 @@ export const OBSERVE_TOOLS = [
234
254
  },
235
255
  {
236
256
  name: ReticleTool.ASSERT,
237
- example: { predicate: { kind: 'signal', name: 'todos:loaded' } },
257
+ example: { predicate: { kind: PredicateKind.SIGNAL, name: 'todos:loaded' } },
238
258
  description: 'Evaluate a predicate (optionally waiting up to timeout_ms). Returns { pass, evidence, failureReason? }. The end of every verify loop. Prefer a { signal } or { net } consequence over { element }/{ text } presence — a passing presence-only assertion returns `advice` because a wrong/healed element can fake it. By default it only counts events since your last act, so a stale buffered signal can never fake a pass; pass `since` (an observe/act cursor) to set the window explicitly.',
239
259
  inputSchema: {
240
- predicate: PredicateSchema.describe('Predicate to evaluate: { signal }, { net }, { element } or a combination.'),
260
+ predicate: PredicateSchema.optional().describe(
261
+ // Every kind, each with the field that carries its argument. Five of the nine were
262
+ // undocumented here, including `route` — and "did submitting the login form navigate away"
263
+ // is the most common thing an agent wants to assert. A field report reached us from an agent
264
+ // that guessed `urlContains` on route (net's spelling) and got unrecognized_keys.
265
+ 'Predicate to evaluate. Kinds: { signal, name } { net, urlContains|method|status|count } ' +
266
+ '{ state, path|equals } { route, pathname (exact) | contains (path+query+hash) } ' +
267
+ '{ element, testid|role|text } { text } { console, level|absent } { animation, name } ' +
268
+ '{ settled } — combine with { allOf | anyOf | not }. Prefer a signal/net/state consequence ' +
269
+ 'over element/text presence.'),
270
+ until: PredicateSchema.optional().describe("Alias for `predicate` (act_and_wait's name)."),
241
271
  timeout_ms: z
242
272
  .number()
243
273
  .optional()
@@ -252,6 +282,10 @@ export const OBSERVE_TOOLS = [
252
282
  pass: z.boolean(),
253
283
  evidence: z.unknown().optional(),
254
284
  failureReason: z.string().optional(),
285
+ observationLost: z
286
+ .boolean()
287
+ .optional()
288
+ .describe('The tab disconnected mid-wait, so this was never observed — the verdict is UNKNOWN, not a failure of the app.'),
255
289
  advice: z
256
290
  .string()
257
291
  .optional()
@@ -287,7 +321,8 @@ export const OBSERVE_TOOLS = [
287
321
  },
288
322
  handler: async (deps, args) => {
289
323
  const session = deps.sessions.resolve(asString(args['sessionId']));
290
- const predicate = PredicateSchema.parse(args['predicate']);
324
+ // `until` is act_and_wait's name for this — see alias-args.ts.
325
+ const predicate = parsePredicate(aliasParam(args, 'predicate', ['until'])['predicate']);
291
326
  const timeout = asNumber(args['timeout_ms']) ?? 0;
292
327
  // Honesty: explicit since wins; else default to the last act's cursor; else the whole buffer.
293
328
  const since = asNumber(args['since']) ?? session.lastAct.cursor() ?? 0;
@@ -306,7 +341,7 @@ export const OBSERVE_TOOLS = [
306
341
  : assertsDerivedIpcStatus(predicate)
307
342
  ? { advice: DERIVED_IPC_STATUS_ADVICE }
308
343
  : {};
309
- const { decision, contradictions, coverage } = await assertVerdict(session, predicate, verdict.pass, since);
344
+ const { decision, contradictions, coverage } = await assertVerdict(session, predicate, verdict.pass, since, verdict.inconclusive, verdict.observationLost);
310
345
  return withControl(session, {
311
346
  ...decision,
312
347
  ...verdict,
@@ -374,7 +409,7 @@ export const OBSERVE_TOOLS = [
374
409
  const method = asString(args['method']);
375
410
  const urlContains = asString(args['urlContains']);
376
411
  const status = asNumber(args['status']);
377
- const ok = typeof args['ok'] === 'boolean' ? args['ok'] : undefined;
412
+ const ok = 'boolean' === typeof args['ok'] ? args['ok'] : undefined;
378
413
  const limit = asNumber(args['limit']);
379
414
  const buffer = bufferEnvelope(session);
380
415
  // Completed calls + unresolved in-flight requests (a hung request shows as pending).
@@ -385,18 +420,21 @@ export const OBSERVE_TOOLS = [
385
420
  }));
386
421
  const matched = allNet.filter((e) => matchNet(e, method, urlContains, status, ok));
387
422
  // zero-match filter returns what DID fire, not a bare [].
388
- if (matched.length === 0 && allNet.length > 0) {
423
+ if (0 === matched.length && allNet.length > 0) {
389
424
  return withSizeCost({ calls: matched, hint: netEmptyHint(allNet), ...buffer });
390
425
  }
391
426
  // Default the cap so an omitted `limit` can't dump a whole flooded session (since defaults to 0).
392
427
  const { events: budgeted, droppedOldest } = applyEventBudget(matched, limit ?? DEFAULT_QUERY_LIMIT);
393
428
  const calls = budgeted.map(projectNetCall);
394
- return withSizeCost({
429
+ // A zero-match FILTER already reports what did fire (netEmptyHint above). Zero calls at all
430
+ // fell through as a bare `[]`, which is indistinguishable from an observer that is not
431
+ // recording — and those need opposite responses. Say the look happened.
432
+ return withSizeCost(noteEmptyRead({
395
433
  calls,
396
434
  ...(droppedOldest > 0 ? { total: matched.length, droppedOldest } : {}),
397
435
  ...bodiesNotCaptured(calls),
398
436
  ...buffer,
399
- });
437
+ }, 'calls', { noun: 'network calls' }));
400
438
  },
401
439
  },
402
440
  {
@@ -404,10 +442,9 @@ export const OBSERVE_TOOLS = [
404
442
  example: { level: 'error' },
405
443
  description: 'Console/error log. Fast path for "were there any errors during this flow?". When a level filter matches nothing, returns a `hint` { totalInWindow, byLevel } so 0 errors is distinguishable from a silent page.',
406
444
  inputSchema: {
407
- level: z
408
- .string()
445
+ level: consoleLevelEnum
409
446
  .optional()
410
- .describe('Log level filter: error | warn | info | log. Omit to return all levels.'),
447
+ .describe(`Log level filter: ${CONSOLE_LEVEL_LIST}. Omit to return all levels.`),
411
448
  since: z
412
449
  .number()
413
450
  .optional()
@@ -451,15 +488,19 @@ export const OBSERVE_TOOLS = [
451
488
  })).filter(isConsoleEvent);
452
489
  const matched = allConsole.filter((e) => matchConsole(e, level));
453
490
  // zero matches at this level → report what levels ARE present (not a bare []).
454
- if (matched.length === 0 && allConsole.length > 0) {
491
+ if (0 === matched.length && allConsole.length > 0) {
455
492
  return withSizeCost({ logs: matched, hint: consoleEmptyHint(allConsole), ...buffer });
456
493
  }
457
494
  // Default the cap so an omitted `limit` can't dump a whole flooded session (since defaults to 0).
458
495
  const { events: budgeted, droppedOldest } = applyEventBudget(matched, limit ?? DEFAULT_QUERY_LIMIT);
459
496
  const logs = budgeted.map(projectConsoleLog);
460
- return withSizeCost(droppedOldest > 0
497
+ // Say the look HAPPENED when it found nothing. A quiet page and a dead console observer both
498
+ // produced `{ logs: [] }`, and "no console errors" is the claim agents lean on most — see
499
+ // observed-nothing.ts, whose own header names this exact case and which every other array-
500
+ // returning read here was already wired to.
501
+ return withSizeCost(noteEmptyRead(droppedOldest > 0
461
502
  ? { logs, total: matched.length, droppedOldest, ...buffer }
462
- : { logs, ...buffer });
503
+ : { logs, ...buffer }, 'logs', { noun: 'console lines' }));
463
504
  },
464
505
  },
465
506
  {
@@ -469,6 +510,17 @@ export const OBSERVE_TOOLS = [
469
510
  outputSchema: {
470
511
  animations: z.array(z.unknown()),
471
512
  },
472
- handler: (deps, args) => commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.ANIMATIONS, {}),
513
+ handler: async (deps, args) => {
514
+ const result = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.ANIMATIONS, {});
515
+ // A page with nothing animating and an observer that is not watching both return `[]`, and the
516
+ // second means the run should not be trusted. Say the look happened.
517
+ return isPlainRecord(result)
518
+ ? noteEmptyRead(result, 'animations', { noun: 'animations running or completed' })
519
+ : result;
520
+ },
473
521
  },
474
522
  ];
523
+ /** Narrow a command result to something noteEmptyRead can annotate. */
524
+ function isPlainRecord(value) {
525
+ return 'object' === typeof value && value !== null && !Array.isArray(value);
526
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Say that the look HAPPENED, when the look found nothing.
3
+ *
4
+ * A quiet page and a dead observer produce the same JSON: `[]`. Reported across seven tools in a
5
+ * field sweep — network with 0 calls, console with 0 logs, reconcile comparing 0, crawl with 0 steps,
6
+ * an empty message inbox — none of which could be told apart from the observer not working. Those
7
+ * need opposite responses: one means "the app did nothing", the other means "stop trusting this run".
8
+ *
9
+ * One shared shape rather than a field per tool: an empty read states the window it watched. "I
10
+ * watched 2000ms and saw nothing" is a finding. `[]` is an absence of information wearing the same
11
+ * clothes as one.
12
+ */
13
+ interface EmptyReadContext {
14
+ /** The observation window, when the read has one. */
15
+ windowMs?: number;
16
+ /** What was being counted, in the agent's words: "console lines", "network calls". */
17
+ noun: string;
18
+ }
19
+ /**
20
+ * Add `observed: true` and a note when `key` holds an EMPTY array. Returns the result untouched
21
+ * otherwise — including when it is a refusal, where "I observed nothing" would be actively
22
+ * misleading on top of an error that already explains itself.
23
+ */
24
+ export declare function noteEmptyRead(result: Record<string, unknown>, key: string, context: EmptyReadContext): Record<string, unknown>;
25
+ export {};
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Say that the look HAPPENED, when the look found nothing.
3
+ *
4
+ * A quiet page and a dead observer produce the same JSON: `[]`. Reported across seven tools in a
5
+ * field sweep — network with 0 calls, console with 0 logs, reconcile comparing 0, crawl with 0 steps,
6
+ * an empty message inbox — none of which could be told apart from the observer not working. Those
7
+ * need opposite responses: one means "the app did nothing", the other means "stop trusting this run".
8
+ *
9
+ * One shared shape rather than a field per tool: an empty read states the window it watched. "I
10
+ * watched 2000ms and saw nothing" is a finding. `[]` is an absence of information wearing the same
11
+ * clothes as one.
12
+ */
13
+ /**
14
+ * Add `observed: true` and a note when `key` holds an EMPTY array. Returns the result untouched
15
+ * otherwise — including when it is a refusal, where "I observed nothing" would be actively
16
+ * misleading on top of an error that already explains itself.
17
+ */
18
+ export function noteEmptyRead(result, key, context) {
19
+ if ('string' === typeof result['error'])
20
+ return result;
21
+ const value = result[key];
22
+ if (!Array.isArray(value) || value.length > 0)
23
+ return result;
24
+ if (result['note'] !== undefined)
25
+ return result;
26
+ const window = context.windowMs === undefined ? '' : ` over the last ${String(context.windowMs)}ms`;
27
+ return {
28
+ ...result,
29
+ // The distinction the field sweep could not make: this is a RESULT, not a failure to look.
30
+ observed: true,
31
+ note: `no ${context.noun}${window} — the observation ran and found none, which is a result, not a missing reading`,
32
+ };
33
+ }
@@ -19,7 +19,7 @@
19
19
  * number that is silently wrong. `count` survives because it is a scalar. Trust it over the array.
20
20
  */
21
21
  export function paginateQueryResult(result, limit, countOnly) {
22
- if (typeof result !== 'object' || result === null)
22
+ if (typeof result !== 'object' || null === result)
23
23
  return result;
24
24
  const record = result;
25
25
  const elements = record['elements'];
@@ -28,7 +28,7 @@ export function paginateQueryResult(result, limit, countOnly) {
28
28
  const counted = record['count'];
29
29
  // Older browser SDKs (and the zero-match hint path) may not carry `count`; fall back rather than
30
30
  // report nothing, but never prefer the array when the browser told us the real number.
31
- const total = typeof counted === 'number' ? counted : elements.length;
31
+ const total = 'number' === typeof counted ? counted : elements.length;
32
32
  const truncatedOnWire = total > elements.length;
33
33
  if (countOnly) {
34
34
  const { elements: _dropped, ...rest } = record;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Accepting the element-query shape the rest of the surface teaches.
3
+ *
4
+ * `reticle_query` asks for a strategy and a value — `{ by: 'text', value: 'Deploy' }`. Every
5
+ * predicate asks for named fields — `{ text: 'Deploy' }`, `{ testid: 'submit' }` — and that is where
6
+ * an agent meets element queries most often (`act_and_wait { until: { kind:'element', query } }`,
7
+ * `reticle_assert`, `wait_for`). Carrying the predicate's spelling to `reticle_query` earns
8
+ * `-32602: Unknown parameter for reticle_query: text`.
9
+ *
10
+ * It is a small thing that lands in an expensive place: of the sessions that called any tool in a
11
+ * day, HALF made exactly one call and stopped. A rejected first call is a bounced session, and the
12
+ * agent has no way to know the rejection was about spelling rather than the page.
13
+ *
14
+ * Translation only — `by`/`value` remain the tool's contract, an explicit pair always wins, and a
15
+ * call carrying neither shape is left alone so the schema error still explains itself rather than
16
+ * being replaced by a confident wrong query.
17
+ */
18
+ export declare function normalizeQueryArgs(args: Record<string, unknown>): Record<string, unknown>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Accepting the element-query shape the rest of the surface teaches.
3
+ *
4
+ * `reticle_query` asks for a strategy and a value — `{ by: 'text', value: 'Deploy' }`. Every
5
+ * predicate asks for named fields — `{ text: 'Deploy' }`, `{ testid: 'submit' }` — and that is where
6
+ * an agent meets element queries most often (`act_and_wait { until: { kind:'element', query } }`,
7
+ * `reticle_assert`, `wait_for`). Carrying the predicate's spelling to `reticle_query` earns
8
+ * `-32602: Unknown parameter for reticle_query: text`.
9
+ *
10
+ * It is a small thing that lands in an expensive place: of the sessions that called any tool in a
11
+ * day, HALF made exactly one call and stopped. A rejected first call is a bounced session, and the
12
+ * agent has no way to know the rejection was about spelling rather than the page.
13
+ *
14
+ * Translation only — `by`/`value` remain the tool's contract, an explicit pair always wins, and a
15
+ * call carrying neither shape is left alone so the schema error still explains itself rather than
16
+ * being replaced by a confident wrong query.
17
+ */
18
+ /** Most specific first: the order Reticle prefers anchors everywhere else (testid is gold). */
19
+ const FIELD_TO_STRATEGY = [
20
+ ['testid', 'testid'],
21
+ ['label', 'label'],
22
+ ['placeholder', 'placeholder'],
23
+ ['alt', 'alt'],
24
+ ['text', 'text'],
25
+ ['role', 'role'],
26
+ ];
27
+ export function normalizeQueryArgs(args) {
28
+ // An explicit pair is the contract; never second-guess it.
29
+ if ('string' === typeof args['by'] && 'string' === typeof args['value'])
30
+ return args;
31
+ for (const [field, strategy] of FIELD_TO_STRATEGY) {
32
+ const value = args[field];
33
+ if ('string' === typeof value && value.length > 0) {
34
+ // `name` stays as-is: with role it is the accessible-name filter the predicate also uses.
35
+ return { ...args, by: strategy, value };
36
+ }
37
+ }
38
+ return args;
39
+ }
@@ -2,18 +2,43 @@
2
2
  * Read / record / replay tools — baselines + diff, recordings + replay, narrate, clock, state,
3
3
  * explore. Split out of tools.ts; assembled back via...READ_TOOLS.
4
4
  */
5
+ import { resolveAnnotateTarget } from '../flows/annotate-target.js';
5
6
  import { z } from 'zod';
6
7
  import { EventType, ReticleCommand, REPLAY_PROGRAM_VERSION, SnapshotMode } from '@reticlehq/core';
7
8
  import { ReticleTool } from './tool-names.js';
8
9
  import { proposeConsequences } from '../oracles/propose-consequences.js';
9
10
  import { replayProgram } from '../flows/replay.js';
10
11
  import { diffLines } from '../project/baselines.js';
11
- import { selectPath, capDepth } from '../session/state-select.js';
12
+ import { selectPath, capDepth, projectComponentState } from '../session/state-select.js';
12
13
  import { costHint } from '../session/output-budget.js';
13
14
  import { buildReactionReport } from '../events/reaction.js';
14
15
  import { asString, asNumber, parseInteractive } from './tools-helpers.js';
15
16
  import { sessionIdShape, commandOrThrow, snapshotTree } from './tool-kit.js';
16
17
  import { bufferEnvelope } from '../session/session-health.js';
18
+ /**
19
+ * What record-stop says about a step that compiled to no anchor at all.
20
+ *
21
+ * `stable: false` does not mean "brittle". It means the compiler found no testid, no accessible
22
+ * role+name and no component/source, so the step is bound to a live `ref` — a handle that dies with
23
+ * the session. The replayer already states this outcome in full (see DEGRADED_STEP_REASON): the step
24
+ * can never resolve on replay.
25
+ *
26
+ * The old wording here — "replay may be brittle (in-session only)" — described a fatal condition as
27
+ * a quality note, so the agent saved the flow and learned the truth several calls later, as an
28
+ * unhealable `anchor_degraded` drift at flow_verify, by which point nothing on screen points back
29
+ * to the element that needed the attribute. Capture time is the only moment the fix is cheap: the
30
+ * element is still on the page and the human is still in the loop.
31
+ */
32
+ function unanchoredWarning(count) {
33
+ return (`${String(count)} step(s) not bound to a testid, an accessible role+name, or a component — ` +
34
+ 'they are pinned to a live ref, so they will replay in THIS session and can never resolve in ' +
35
+ 'another one. Add a data-testid to those elements and record the flow again, or accept that ' +
36
+ 'this recording is single-session only.');
37
+ }
38
+ /** Severities the presenter HUD can render. */
39
+ const HUD_LEVELS = ['info', 'warn', 'error'];
40
+ const HUD_LEVEL_LIST = HUD_LEVELS.join(' | ');
41
+ const hudLevelEnum = z.enum(HUD_LEVELS);
17
42
  export const READ_TOOLS = [
18
43
  {
19
44
  name: ReticleTool.BASELINE_SAVE,
@@ -64,7 +89,11 @@ export const READ_TOOLS = [
64
89
  buffer: z.unknown().optional(),
65
90
  },
66
91
  handler: async (deps, args) => {
67
- const name = asString(args['baseline']) ?? 'default';
92
+ // `name` too: the merged reticle_baseline family made this required field optional, and the
93
+ // SIBLING that creates a baseline calls it `name` — its description even says to reuse it
94
+ // here. So `{ action:"diff", name:"x" }` is a valid call that used to look up 'default' and
95
+ // report "no baseline named 'default'", naming something the caller never mentioned.
96
+ const name = asString(args['baseline']) ?? asString(args['name']) ?? 'default';
68
97
  const base = deps.baselines.get(name);
69
98
  if (base === undefined)
70
99
  throw new Error(`no baseline named '${name}'`);
@@ -127,10 +156,17 @@ export const READ_TOOLS = [
127
156
  },
128
157
  handler: (deps, args) => {
129
158
  const session = deps.sessions.resolve(asString(args['sessionId']));
130
- const name = asString(args['recordingName']) ?? 'default';
159
+ // The recording actually RUNNING when there is exactly one, not the literal name `default` —
160
+ // the same defaulting that made `annotate` report "no steps" against a recording full of them.
161
+ // With several running, `default` stays the documented answer rather than a guess.
162
+ const name = resolveAnnotateTarget(asString(args['recordingName']), deps.recordings.active());
131
163
  const rec = deps.recordings.stop(name);
132
- if (rec === undefined)
133
- throw new Error(`no active recording named '${name}'`);
164
+ if (rec === undefined) {
165
+ const active = deps.recordings.active();
166
+ throw new Error(0 === active.length
167
+ ? `no active recording named '${name}' — none is in progress`
168
+ : `no active recording named '${name}'; in progress: ${active.map((r) => `'${r}'`).join(', ')}`);
169
+ }
134
170
  const events = session.eventsSince(rec.cursor);
135
171
  const program = {
136
172
  name,
@@ -147,7 +183,7 @@ export const READ_TOOLS = [
147
183
  program,
148
184
  ...(unstable > 0
149
185
  ? {
150
- warning: `${String(unstable)} step(s) not bound to a testid; replay may be brittle (in-session only)`,
186
+ warning: unanchoredWarning(unstable),
151
187
  }
152
188
  : {}),
153
189
  ...(proposedConsequences.length > 0 ? { proposedConsequences } : {}),
@@ -186,7 +222,7 @@ export const READ_TOOLS = [
186
222
  throw new Error(`no compiled recording named '${name}'`);
187
223
  const session = deps.sessions.resolve(asString(args['sessionId']));
188
224
  const since = session.elapsed();
189
- const steps = await replayProgram(session, program, args['confirmDangerous'] === true);
225
+ const steps = await replayProgram(session, program, true === args['confirmDangerous']);
190
226
  return { recordingName: name, since, steps, ok: steps.every((s) => s.ok) };
191
227
  },
192
228
  },
@@ -197,10 +233,11 @@ export const READ_TOOLS = [
197
233
  text: z
198
234
  .string()
199
235
  .describe('Short sentence describing your next action, shown on the presenter HUD for the developer watching.'),
200
- level: z
201
- .string()
236
+ // Derived, like every other advertised vocabulary here: a free string let a typo through to
237
+ // a HUD that then rendered an unknown severity.
238
+ level: hudLevelEnum
202
239
  .optional()
203
- .describe('Display severity: info | warn | error. Default: info.'),
240
+ .describe(`Display severity: ${HUD_LEVEL_LIST}. Default: info.`),
204
241
  ...sessionIdShape,
205
242
  },
206
243
  outputSchema: { ok: z.boolean() },
@@ -227,9 +264,11 @@ export const READ_TOOLS = [
227
264
  reset: z.boolean().optional().describe('Restore the real clock.'),
228
265
  ...sessionIdShape,
229
266
  },
267
+ // The command returns `{ frozen }`. It used to declare `{ ok, elapsed }` — neither of which any
268
+ // clock code path produces — and MCP strips undeclared fields from structuredContent, so a
269
+ // successful freeze and a failed one both validated to `{}` and became indistinguishable.
230
270
  outputSchema: {
231
- ok: z.boolean().optional(),
232
- elapsed: z.number().optional(),
271
+ frozen: z.boolean().optional(),
233
272
  },
234
273
  handler: (deps, args) => commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.CLOCK, {
235
274
  freeze: args['freeze'],
@@ -275,7 +314,21 @@ export const READ_TOOLS = [
275
314
  // concludes the key it wants is absent when it is merely past the cap.
276
315
  totalKeys: z.number().optional(),
277
316
  component: z
278
- .object({ ok: z.boolean(), reason: z.string().optional(), state: z.unknown().optional() })
317
+ .object({
318
+ ok: z.boolean(),
319
+ reason: z.string().optional(),
320
+ state: z.unknown().optional(),
321
+ // The component name and the projected hook VALUES — declared so a schema-strict client
322
+ // keeps them (they were the point of passing `ref` at all).
323
+ component: z.string().optional(),
324
+ hooks: z.array(z.unknown()).optional(),
325
+ // Present only when effect hooks were projected out. Declared for the same reason as the
326
+ // store truncation report below: a shortened list with no marker reads as a complete one.
327
+ truncation: z
328
+ .object({ droppedItems: z.number(), note: z.string() })
329
+ .optional()
330
+ .describe('Present only when effect hooks were dropped — `hooks` is a projection.'),
331
+ })
279
332
  .optional(),
280
333
  // Truncation report — present ONLY when a transport cap trimmed the value. Declared so a
281
334
  // schema-strict client on the `full` profile KEEPS it: this is a false-green GUARD, and dropping
@@ -298,23 +351,31 @@ export const READ_TOOLS = [
298
351
  // Forward path/depth so a CURRENT browser SDK scopes the read IN-PAGE, before the transport —
299
352
  // the value never gets size-truncated in transit. (An older SDK ignores them and returns the
300
353
  // whole store; we then scope server-side below as a back-compat fallback.)
301
- const result = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.STATE_READ, {
354
+ const raw = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.STATE_READ, {
302
355
  ref: args['ref'],
303
356
  store,
304
357
  path,
305
358
  depth,
306
359
  });
360
+ // Project the component hook read BEFORE anything else touches it: React hands back the raw
361
+ // fiber hook list, whose effect entries are chained null-filled internals an agent cannot act
362
+ // on (measured: 2632 -> 1459 bytes on a five-state/three-effect component). Done here rather
363
+ // than in the SDK so an older browser build gets the same projection. The drop is disclosed
364
+ // on `component.truncation`.
365
+ const result = 'object' === typeof raw && null !== raw && 'component' in raw
366
+ ? { ...raw, component: projectComponentState(raw.component) }
367
+ : raw;
307
368
  // Normalize storeNames to a string[] regardless of how the wire delivered it — the
308
369
  // outputSchema requires an array, and a non-array here makes MCP reject the whole result
309
370
  // (so the agent gets nothing instead of the state). Defensive: a string becomes a 1-element array.
310
371
  const root = result;
311
372
  const names = Array.isArray(root.storeNames)
312
- ? root.storeNames.filter((n) => typeof n === 'string')
313
- : typeof root.storeNames === 'string' && root.storeNames.length > 0
373
+ ? root.storeNames.filter((n) => 'string' === typeof n)
374
+ : 'string' === typeof root.storeNames && root.storeNames.length > 0
314
375
  ? [root.storeNames]
315
376
  : [];
316
377
  // The browser already scoped it in-page (the `found` shape) — pass through, just safe storeNames.
317
- if (typeof root.found === 'boolean') {
378
+ if ('boolean' === typeof root.found) {
318
379
  return { ...root, storeNames: names };
319
380
  }
320
381
  if (path === undefined && depth === undefined) {
@@ -372,7 +433,7 @@ export const READ_TOOLS = [
372
433
  // The walk stops at its node cap and returns a document-order prefix, so an inventory taken
373
434
  // from a big page is a floor, not a census — and this is the tool crawl's description points
374
435
  // agents at first for "a non-destructive list of what is here".
375
- ...(snap.truncated === true ? { truncated: true } : {}),
436
+ ...(true === snap.truncated ? { truncated: true } : {}),
376
437
  consoleErrors,
377
438
  hint: 'act on each ref, observe the reaction, and report failed requests / console errors / dead controls',
378
439
  // Buffer-honesty: the console-error count spans the whole buffer, which evicts — signal it.
@@ -412,13 +473,13 @@ export const READ_TOOLS = [
412
473
  const area = asString(args['area']);
413
474
  const key = asString(args['key']);
414
475
  const result = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.STORAGE_READ, area !== undefined ? { area } : {});
415
- const data = (typeof result === 'object' && result !== null ? result : {});
476
+ const data = ('object' === typeof result && result !== null ? result : {});
416
477
  if (key === undefined)
417
478
  return data;
418
479
  // Look up the key in the scoped area, or across all three areas when none was named.
419
480
  const areas = area !== undefined ? [data] : [data['local'], data['session'], data['cookies']];
420
481
  for (const a of areas) {
421
- if (typeof a === 'object' && a !== null && key in a) {
482
+ if ('object' === typeof a && a !== null && key in a) {
422
483
  return { area, key, value: String(a[key]), found: true };
423
484
  }
424
485
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The native-input attempt — everything that decides whether a pointer action is driven through a
3
+ * real input provider or falls back to the occlusion-honest synthetic path, and WHY.
4
+ *
5
+ * Split out of act-tools.ts along its natural seam: the act tools care only about the outcome
6
+ * (`result` defined = it went native), never about provider availability, box resolution,
7
+ * drag-target inspection or the reason taxonomy.
8
+ */
9
+ import { ActionType, InputModeReason } from '@reticlehq/core';
10
+ import type { Session } from '../session/session.js';
11
+ import { type ToolDeps } from './tool-kit.js';
12
+ interface RealActResult {
13
+ /** Defined only on a successful native action; `undefined` means the synthetic path runs. */
14
+ result: unknown;
15
+ settled: boolean;
16
+ /** Set when a provider was available but threw — surfaces the fallback to the agent. */
17
+ fellBack?: boolean;
18
+ /** Why we went synthetic despite a configured provider (field bug #2: never a silent fallback). */
19
+ reason?: InputModeReason;
20
+ }
21
+ /**
22
+ * Attempt to drive a pointer action via native input. Returns a synthetic outcome (with a
23
+ * `reason` when a provider is configured) whenever the synthetic path should run — no matching
24
+ * page, unresolvable box, declined, etc. A throw inside the provider becomes a synthetic fallback
25
+ * flagged with `fellBack`. `result` is defined only on a real success.
26
+ */
27
+ export declare function tryRealInput(deps: ToolDeps, session: Session, ref: string, action: ActionType, args: Record<string, unknown>): Promise<RealActResult>;
28
+ export {};