@reticlehq/server 2.3.0 → 2.5.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 (424) hide show
  1. package/SKILL.md +702 -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} +1 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +105 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
  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-doctor.js → cli/cli-doctor.js} +9 -2
  20. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  21. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  22. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  23. package/dist/cli/cli-launch.js +171 -0
  24. package/dist/cli/cli-parse.d.ts +143 -0
  25. package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
  26. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
  27. package/dist/{cli-port.js → cli/cli-port.js} +29 -8
  28. package/dist/cli/cli-update-commands.d.ts +4 -0
  29. package/dist/cli/cli-update-commands.js +93 -0
  30. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  31. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  32. package/dist/cli.d.ts +2 -2
  33. package/dist/cli.js +182 -93
  34. package/dist/cloud/cloud-config.d.ts +0 -1
  35. package/dist/cloud/cloud-config.js +9 -9
  36. package/dist/cloud/cloud-sync.d.ts +1 -7
  37. package/dist/cloud/cloud-sync.js +12 -12
  38. package/dist/crawl/crawl-empty.d.ts +16 -0
  39. package/dist/crawl/crawl-empty.js +24 -0
  40. package/dist/crawl/crawl-tools.js +4 -1
  41. package/dist/crawl/crawl.js +26 -7
  42. package/dist/daemon/daemon-resilience.d.ts +49 -0
  43. package/dist/daemon/daemon-resilience.js +191 -0
  44. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  45. package/dist/daemon/daemon-usefulness.js +62 -0
  46. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  47. package/dist/{daemon.js → daemon/daemon.js} +80 -48
  48. package/dist/daemon/idle-grace.d.ts +32 -0
  49. package/dist/daemon/idle-grace.js +41 -0
  50. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
  51. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
  52. package/dist/domain/domain-model.js +2 -2
  53. package/dist/events/body-failures.js +8 -8
  54. package/dist/events/contradictions.d.ts +1 -1
  55. package/dist/events/contradictions.js +60 -10
  56. package/dist/events/echo-mismatch.js +10 -10
  57. package/dist/events/event-filters.js +6 -6
  58. package/dist/events/json-salvage.d.ts +2 -1
  59. package/dist/events/json-salvage.js +6 -6
  60. package/dist/events/observed-in-window.d.ts +12 -0
  61. package/dist/events/observed-in-window.js +21 -0
  62. package/dist/events/predicate-eval.d.ts +43 -20
  63. package/dist/events/predicate-eval.js +159 -55
  64. package/dist/events/predicate.d.ts +2 -2
  65. package/dist/events/predicate.js +112 -35
  66. package/dist/events/reconcile.js +5 -5
  67. package/dist/events/ring-buffer.js +1 -1
  68. package/dist/events/stale-response.js +6 -6
  69. package/dist/events/testid-near-miss.d.ts +22 -0
  70. package/dist/events/testid-near-miss.js +33 -0
  71. package/dist/events/unit-mismatch.js +8 -8
  72. package/dist/flows/affected-tools.js +19 -4
  73. package/dist/flows/affected.js +1 -1
  74. package/dist/flows/annotate-target.d.ts +1 -0
  75. package/dist/flows/annotate-target.js +20 -0
  76. package/dist/flows/annotate-tools.js +25 -3
  77. package/dist/flows/annotate.js +17 -4
  78. package/dist/flows/assertion-integrity.d.ts +2 -1
  79. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  80. package/dist/flows/assertion-tiers-store.js +2 -2
  81. package/dist/flows/attributed-failure.d.ts +17 -0
  82. package/dist/flows/attributed-failure.js +20 -0
  83. package/dist/flows/buddy-status.d.ts +2 -1
  84. package/dist/flows/buddy-status.js +2 -2
  85. package/dist/flows/coverage.d.ts +3 -2
  86. package/dist/flows/coverage.js +2 -2
  87. package/dist/flows/decision.d.ts +0 -6
  88. package/dist/flows/decision.js +64 -8
  89. package/dist/flows/empty-flow.d.ts +6 -0
  90. package/dist/flows/empty-flow.js +24 -0
  91. package/dist/flows/flake-store.d.ts +0 -1
  92. package/dist/flows/flake-store.js +1 -1
  93. package/dist/flows/flake.d.ts +2 -1
  94. package/dist/flows/flake.js +2 -2
  95. package/dist/flows/flow-classify.js +12 -0
  96. package/dist/flows/flow-replay-run.js +4 -4
  97. package/dist/flows/flow-replay.d.ts +5 -7
  98. package/dist/flows/flow-replay.js +114 -47
  99. package/dist/flows/flow-report.js +1 -1
  100. package/dist/flows/flow-scope.d.ts +1 -6
  101. package/dist/flows/flow-step-runners.d.ts +26 -6
  102. package/dist/flows/flow-step-runners.js +157 -6
  103. package/dist/flows/flow-success.js +20 -15
  104. package/dist/flows/flow-tools.js +67 -13
  105. package/dist/flows/flows.js +15 -11
  106. package/dist/flows/gate.d.ts +4 -3
  107. package/dist/flows/gate.js +1 -1
  108. package/dist/flows/heal-run.d.ts +0 -8
  109. package/dist/flows/heal-run.js +5 -5
  110. package/dist/flows/heal.js +3 -3
  111. package/dist/flows/predicate-to-expect.d.ts +38 -0
  112. package/dist/flows/predicate-to-expect.js +95 -0
  113. package/dist/flows/replay.d.ts +16 -1
  114. package/dist/flows/replay.js +58 -22
  115. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  116. package/dist/flows/role-anchor-nearest.js +45 -0
  117. package/dist/flows/server-verify.d.ts +1 -1
  118. package/dist/flows/server-verify.js +5 -5
  119. package/dist/flows/verify-change-tools.js +32 -10
  120. package/dist/flows/verify-change-verdict.d.ts +15 -0
  121. package/dist/flows/verify-change-verdict.js +25 -0
  122. package/dist/flows/watch-batcher.d.ts +3 -2
  123. package/dist/honesty/already-true.d.ts +2 -0
  124. package/dist/honesty/already-true.js +31 -0
  125. package/dist/honesty/blind-spots.d.ts +16 -3
  126. package/dist/honesty/blind-spots.js +24 -12
  127. package/dist/honesty/honesty.d.ts +3 -2
  128. package/dist/honesty/honesty.js +4 -4
  129. package/dist/honesty/uncaptured-bodies.js +1 -1
  130. package/dist/honesty/unread-outcome.js +2 -2
  131. package/dist/honesty/verified.d.ts +22 -3
  132. package/dist/honesty/verified.js +48 -8
  133. package/dist/http-server.js +14 -7
  134. package/dist/hunt/hunt-report.d.ts +2 -1
  135. package/dist/hunt/hunt-report.js +2 -2
  136. package/dist/index.d.ts +8 -6
  137. package/dist/index.js +32 -25
  138. package/dist/init/agent-rules.d.ts +2 -1
  139. package/dist/init/agent-rules.js +45 -7
  140. package/dist/init/app-choice.d.ts +20 -0
  141. package/dist/init/app-choice.js +25 -0
  142. package/dist/init/astro-host.d.ts +21 -0
  143. package/dist/init/astro-host.js +27 -0
  144. package/dist/init/astro-patch.d.ts +16 -0
  145. package/dist/init/astro-patch.js +240 -0
  146. package/dist/init/capabilities.d.ts +17 -0
  147. package/dist/init/capabilities.js +54 -0
  148. package/dist/init/cra.d.ts +37 -0
  149. package/dist/init/cra.js +90 -0
  150. package/dist/init/cursor.js +39 -4
  151. package/dist/init/desktop-doctor.d.ts +3 -2
  152. package/dist/init/desktop-doctor.js +4 -4
  153. package/dist/init/detect.d.ts +26 -0
  154. package/dist/init/detect.js +78 -5
  155. package/dist/init/mcp-registered.d.ts +12 -0
  156. package/dist/init/mcp-registered.js +14 -0
  157. package/dist/init/mcp.d.ts +27 -2
  158. package/dist/init/mcp.js +40 -4
  159. package/dist/init/next-patch.d.ts +39 -0
  160. package/dist/init/next-patch.js +196 -0
  161. package/dist/init/node-io.js +48 -6
  162. package/dist/init/patch-kind.d.ts +20 -0
  163. package/dist/init/patch-kind.js +10 -0
  164. package/dist/init/plan-framework.d.ts +50 -0
  165. package/dist/init/plan-framework.js +341 -0
  166. package/dist/init/plan.d.ts +119 -6
  167. package/dist/init/plan.js +230 -131
  168. package/dist/init/project-id.d.ts +5 -9
  169. package/dist/init/project-id.js +10 -22
  170. package/dist/init/run.d.ts +22 -0
  171. package/dist/init/run.js +393 -28
  172. package/dist/init/slash-command.d.ts +27 -0
  173. package/dist/init/slash-command.js +61 -0
  174. package/dist/init/snippets.d.ts +41 -3
  175. package/dist/init/snippets.js +185 -10
  176. package/dist/init/vite-config.d.ts +4 -10
  177. package/dist/init/vite-config.js +15 -7
  178. package/dist/init/workspace-apps.d.ts +23 -0
  179. package/dist/init/workspace-apps.js +68 -0
  180. package/dist/input/driven-redaction.d.ts +15 -0
  181. package/dist/input/driven-redaction.js +76 -0
  182. package/dist/input/network-detail.d.ts +9 -3
  183. package/dist/input/network-detail.js +43 -23
  184. package/dist/input/network-mock-tools.js +2 -2
  185. package/dist/input/network-mock.js +4 -4
  186. package/dist/input/real-input.js +8 -3
  187. package/dist/input/scroll-find.js +2 -2
  188. package/dist/input/scroll-tools.d.ts +0 -5
  189. package/dist/input/scroll-tools.js +11 -4
  190. package/dist/input/viewport-tools.js +2 -2
  191. package/dist/journal/ambient-store.d.ts +0 -2
  192. package/dist/journal/ambient-store.js +1 -1
  193. package/dist/journal/ambient.js +1 -1
  194. package/dist/journal/attach-journal.d.ts +2 -1
  195. package/dist/journal/deviation-report.js +6 -6
  196. package/dist/journal/deviation-service.js +1 -1
  197. package/dist/journal/envelope-store.d.ts +0 -2
  198. package/dist/journal/envelope-store.js +1 -1
  199. package/dist/journal/envelope.d.ts +2 -3
  200. package/dist/journal/envelope.js +3 -3
  201. package/dist/journal/journal-query.js +1 -1
  202. package/dist/journal/journal-recorder.d.ts +2 -1
  203. package/dist/journal/journal-recorder.js +2 -2
  204. package/dist/journal/rollups.js +9 -9
  205. package/dist/journal/session-end.d.ts +2 -1
  206. package/dist/journal/session-journal.js +4 -4
  207. package/dist/license/license.d.ts +0 -14
  208. package/dist/license/license.js +8 -8
  209. package/dist/log.d.ts +10 -0
  210. package/dist/log.js +11 -1
  211. package/dist/mcp/mcp-is-error.d.ts +13 -0
  212. package/dist/mcp/mcp-is-error.js +18 -0
  213. package/dist/mcp/mcp-outage.d.ts +31 -0
  214. package/dist/mcp/mcp-outage.js +48 -0
  215. package/dist/mcp/mcp-proxy.d.ts +124 -0
  216. package/dist/mcp/mcp-proxy.js +697 -0
  217. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  218. package/dist/{mcp.js → mcp/mcp.js} +110 -35
  219. package/dist/mcp/proxy-handshake.d.ts +3 -0
  220. package/dist/mcp/proxy-handshake.js +54 -0
  221. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  222. package/dist/mcp/proxy-lifecycle.js +70 -0
  223. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  224. package/dist/mcp/tool-catalog-cache.js +55 -0
  225. package/dist/oracles/propose-consequences.d.ts +2 -1
  226. package/dist/oracles/propose-consequences.js +16 -11
  227. package/dist/oracles/self-instrument.d.ts +2 -1
  228. package/dist/oracles/self-instrument.js +1 -1
  229. package/dist/phenomena/phenomena.js +3 -3
  230. package/dist/platform.d.ts +16 -0
  231. package/dist/platform.js +16 -0
  232. package/dist/pool/playwright-launcher.d.ts +15 -2
  233. package/dist/pool/playwright-launcher.js +21 -2
  234. package/dist/project/fs-port.js +1 -1
  235. package/dist/project/project-tools.js +2 -2
  236. package/dist/runs/build-verification-run.js +1 -1
  237. package/dist/runs/repair-prompt.d.ts +0 -6
  238. package/dist/runs/repair-prompt.js +3 -3
  239. package/dist/runs/risk-classify.js +1 -1
  240. package/dist/runs/run-diff.d.ts +0 -2
  241. package/dist/runs/run-diff.js +3 -3
  242. package/dist/runs/run-tools.js +2 -2
  243. package/dist/runs/verification-sync.js +2 -2
  244. package/dist/runs/verify-http.js +1 -1
  245. package/dist/runs/verify-server.js +1 -1
  246. package/dist/session/command-timeout.d.ts +11 -3
  247. package/dist/session/command-timeout.js +2 -2
  248. package/dist/session/control-envelope.d.ts +6 -0
  249. package/dist/session/control-envelope.js +7 -1
  250. package/dist/session/dev-server-probe.d.ts +13 -0
  251. package/dist/session/dev-server-probe.js +52 -0
  252. package/dist/session/last-act.d.ts +17 -3
  253. package/dist/session/last-act.js +16 -4
  254. package/dist/session/live-control-tools.js +7 -2
  255. package/dist/session/live-control.js +1 -1
  256. package/dist/session/looks-like-dev-server.d.ts +14 -0
  257. package/dist/session/looks-like-dev-server.js +25 -0
  258. package/dist/session/no-session-diagnosis.d.ts +38 -0
  259. package/dist/session/no-session-diagnosis.js +70 -0
  260. package/dist/session/no-session-watch.d.ts +19 -0
  261. package/dist/session/no-session-watch.js +61 -0
  262. package/dist/session/observed-state.d.ts +4 -0
  263. package/dist/session/observed-state.js +19 -1
  264. package/dist/session/output-budget.js +1 -1
  265. package/dist/session/session-health.d.ts +8 -1
  266. package/dist/session/session-health.js +8 -7
  267. package/dist/session/session-info.d.ts +26 -7
  268. package/dist/session/session-info.js +42 -1
  269. package/dist/session/session-lease.d.ts +0 -11
  270. package/dist/session/session-lease.js +1 -1
  271. package/dist/session/session-manager.d.ts +3 -0
  272. package/dist/session/session-manager.js +58 -9
  273. package/dist/session/session-readiness.d.ts +1 -1
  274. package/dist/session/session-readiness.js +6 -1
  275. package/dist/session/session-reaper.js +1 -1
  276. package/dist/session/session-reconnect.d.ts +32 -0
  277. package/dist/session/session-reconnect.js +42 -0
  278. package/dist/session/session.d.ts +20 -2
  279. package/dist/session/session.js +76 -26
  280. package/dist/session/state-select.d.ts +1 -1
  281. package/dist/session/state-select.js +1 -1
  282. package/dist/status-payload.d.ts +19 -0
  283. package/dist/status-payload.js +11 -0
  284. package/dist/telemetry/argument-shape.js +10 -1
  285. package/dist/telemetry/browser-mode.d.ts +27 -0
  286. package/dist/telemetry/browser-mode.js +33 -0
  287. package/dist/telemetry/bug-found.d.ts +17 -21
  288. package/dist/telemetry/bug-found.js +120 -8
  289. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  290. package/dist/telemetry/cli-telemetry.js +27 -4
  291. package/dist/telemetry/daemon-telemetry.d.ts +19 -0
  292. package/dist/telemetry/daemon-telemetry.js +12 -2
  293. package/dist/telemetry/dev-repo.js +4 -4
  294. package/dist/telemetry/error-fingerprint.js +21 -2
  295. package/dist/telemetry/feedback-cli.d.ts +1 -1
  296. package/dist/telemetry/feedback-cli.js +4 -4
  297. package/dist/telemetry/feedback-context.d.ts +22 -0
  298. package/dist/telemetry/feedback-context.js +41 -1
  299. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  300. package/dist/telemetry/feedback-delivery.js +30 -0
  301. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  302. package/dist/telemetry/feedback-outbox.js +87 -0
  303. package/dist/telemetry/feedback.d.ts +22 -1
  304. package/dist/telemetry/feedback.js +67 -8
  305. package/dist/telemetry/git-facts.js +1 -1
  306. package/dist/telemetry/session-metrics.d.ts +2 -0
  307. package/dist/telemetry/session-metrics.js +68 -6
  308. package/dist/telemetry/telemetry.d.ts +27 -2
  309. package/dist/telemetry/telemetry.js +119 -29
  310. package/dist/telemetry/verification-of.d.ts +22 -0
  311. package/dist/telemetry/verification-of.js +84 -0
  312. package/dist/tools/act-capsule.js +2 -2
  313. package/dist/tools/act-danger.d.ts +12 -0
  314. package/dist/tools/act-danger.js +20 -3
  315. package/dist/tools/act-helpers.js +4 -4
  316. package/dist/tools/act-tools.d.ts +0 -8
  317. package/dist/tools/act-tools.js +84 -115
  318. package/dist/tools/act-view.js +7 -7
  319. package/dist/tools/alias-args.d.ts +17 -0
  320. package/dist/tools/alias-args.js +25 -0
  321. package/dist/tools/assert-grade.js +9 -9
  322. package/dist/tools/assert-verdict.d.ts +3 -1
  323. package/dist/tools/assert-verdict.js +8 -5
  324. package/dist/tools/browser-tools.js +30 -11
  325. package/dist/tools/contract-tools.js +1 -1
  326. package/dist/tools/coverage-identity.d.ts +34 -0
  327. package/dist/tools/coverage-identity.js +46 -0
  328. package/dist/tools/coverage-tools.js +10 -14
  329. package/dist/tools/dynamic-tools.d.ts +2 -1
  330. package/dist/tools/dynamic-tools.js +90 -18
  331. package/dist/tools/error-recovery.d.ts +20 -2
  332. package/dist/tools/error-recovery.js +179 -4
  333. package/dist/tools/feedback-tools.d.ts +12 -1
  334. package/dist/tools/feedback-tools.js +28 -5
  335. package/dist/tools/invoke-tool.d.ts +0 -6
  336. package/dist/tools/invoke-tool.js +57 -32
  337. package/dist/tools/lease-hint.d.ts +17 -0
  338. package/dist/tools/lease-hint.js +24 -0
  339. package/dist/tools/lease-tools.js +6 -5
  340. package/dist/tools/merge-tools.d.ts +10 -1
  341. package/dist/tools/merge-tools.js +9 -3
  342. package/dist/tools/merged-name-redirect.d.ts +13 -0
  343. package/dist/tools/merged-name-redirect.js +55 -0
  344. package/dist/tools/navigate-result.d.ts +17 -0
  345. package/dist/tools/navigate-result.js +31 -0
  346. package/dist/tools/observe-tools.js +54 -20
  347. package/dist/tools/observed-nothing.d.ts +25 -0
  348. package/dist/tools/observed-nothing.js +33 -0
  349. package/dist/tools/query-paginate.js +2 -2
  350. package/dist/tools/query-shape.d.ts +18 -0
  351. package/dist/tools/query-shape.js +39 -0
  352. package/dist/tools/read-tools.js +85 -20
  353. package/dist/tools/real-input-attempt.d.ts +28 -0
  354. package/dist/tools/real-input-attempt.js +88 -0
  355. package/dist/tools/reconcile-tools.js +7 -7
  356. package/dist/tools/reload-result.d.ts +14 -0
  357. package/dist/tools/reload-result.js +29 -0
  358. package/dist/tools/snapshot-delta.d.ts +4 -5
  359. package/dist/tools/snapshot-delta.js +0 -0
  360. package/dist/tools/tool-kit.d.ts +8 -0
  361. package/dist/tools/tool-surface.d.ts +83 -0
  362. package/dist/tools/tool-surface.js +204 -0
  363. package/dist/tools/tools-helpers.js +6 -6
  364. package/dist/tools/tools.d.ts +15 -0
  365. package/dist/tools/tools.js +53 -17
  366. package/dist/tools/unadvertised-help.d.ts +5 -0
  367. package/dist/tools/unadvertised-help.js +39 -0
  368. package/dist/trace.d.ts +69 -0
  369. package/dist/trace.js +150 -0
  370. package/dist/update/nudge-credit.d.ts +4 -0
  371. package/dist/update/nudge-credit.js +53 -0
  372. package/dist/update/sdk-sync.d.ts +21 -0
  373. package/dist/update/sdk-sync.js +42 -0
  374. package/dist/update/update-checker.js +3 -3
  375. package/dist/update/update-nudge.d.ts +63 -6
  376. package/dist/update/update-nudge.js +128 -15
  377. package/dist/update/updater.d.ts +1 -9
  378. package/dist/update/updater.js +13 -8
  379. package/dist/version/peer-announce.d.ts +4 -0
  380. package/dist/version/peer-announce.js +30 -0
  381. package/dist/{server-version.js → version/server-version.js} +5 -1
  382. package/dist/version/version-nudge.d.ts +28 -0
  383. package/dist/version/version-nudge.js +40 -0
  384. package/dist/version/version-skew.d.ts +66 -0
  385. package/dist/version/version-skew.js +71 -0
  386. package/dist/visual/visual-diff.js +2 -2
  387. package/dist/visual/visual-tools.js +4 -4
  388. package/docs/agent-cheatsheet.md +85 -0
  389. package/docs/architecture.md +113 -0
  390. package/docs/benchmarks.md +255 -0
  391. package/docs/debugging.md +102 -0
  392. package/docs/deploy-checks.md +69 -0
  393. package/docs/desktop-apps.md +256 -0
  394. package/docs/enterprise.md +62 -0
  395. package/docs/fixtures.md +28 -0
  396. package/docs/flows.md +166 -0
  397. package/docs/getting-started.md +469 -0
  398. package/docs/human-control.md +63 -0
  399. package/docs/integration-patterns.md +171 -0
  400. package/docs/local-registry.md +126 -0
  401. package/docs/mint.json +61 -0
  402. package/docs/multi-agent-testing.md +55 -0
  403. package/docs/platform-integration.md +122 -0
  404. package/docs/telemetry-contract.md +214 -0
  405. package/docs/telemetry.md +184 -0
  406. package/docs/testing.md +82 -0
  407. package/docs/token-efficiency.md +80 -0
  408. package/docs/usage.md +1046 -0
  409. package/package.json +8 -5
  410. package/dist/cli-launch.js +0 -112
  411. package/dist/cli-parse.d.ts +0 -140
  412. package/dist/daemon-resilience.d.ts +0 -22
  413. package/dist/daemon-resilience.js +0 -85
  414. package/dist/mcp-proxy.d.ts +0 -35
  415. package/dist/mcp-proxy.js +0 -190
  416. package/dist/tools/profiles.d.ts +0 -37
  417. package/dist/tools/profiles.js +0 -139
  418. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  419. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  420. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  421. /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
  422. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  423. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  424. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -2,8 +2,10 @@
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';
@@ -54,11 +56,21 @@ function lastActSourceOnFailure(session, pass) {
54
56
  * really do interleave; it is only redundant at this boundary.
55
57
  */
56
58
  function withoutConstantSessionId(event) {
57
- if (typeof event !== 'object' || event === null)
59
+ if (typeof event !== 'object' || null === event)
58
60
  return event;
59
61
  const { sessionId: _sessionId, ...rest } = event;
60
62
  return rest;
61
63
  }
64
+ /**
65
+ * The console levels, derived from core's console EventTypes — see CONSOLE_LEVELS.
66
+ *
67
+ * A free string here filtered by building `console.${level}`, so `level:'ERROR'` or `level:'fatal'`
68
+ * matched nothing and returned an empty log list. On a page that HAS logs the zero-match hint saves
69
+ * it (it reports which levels are present), but on a quiet page the answer is identical to a genuine
70
+ * all-clear. Refusing the value outright is the only reading with no ambiguity.
71
+ */
72
+ const CONSOLE_LEVEL_LIST = CONSOLE_LEVELS.join(' | ');
73
+ const consoleLevelEnum = z.enum(CONSOLE_LEVELS);
62
74
  export const OBSERVE_TOOLS = [
63
75
  {
64
76
  name: ReticleTool.OBSERVE,
@@ -180,10 +192,12 @@ export const OBSERVE_TOOLS = [
180
192
  },
181
193
  {
182
194
  name: ReticleTool.WAIT_FOR,
183
- example: { predicate: { kind: 'state', path: 'todos.length', equals: 3 } },
195
+ example: { predicate: { kind: PredicateKind.STATE, path: 'todos.length', equals: 3 } },
184
196
  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
197
  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.'),
198
+ 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.'),
199
+ // Same concept, the neighbouring tool's name. See alias-args.ts.
200
+ until: PredicateSchema.optional().describe("Alias for `predicate` (act_and_wait's name)."),
187
201
  timeout_ms: z.number().optional().describe('Maximum wait in milliseconds. Default: 4000.'),
188
202
  since: z
189
203
  .number()
@@ -218,7 +232,8 @@ export const OBSERVE_TOOLS = [
218
232
  },
219
233
  handler: async (deps, args) => {
220
234
  const session = deps.sessions.resolve(asString(args['sessionId']));
221
- const predicate = PredicateSchema.parse(args['predicate']);
235
+ // `until` is act_and_wait's name for this — see alias-args.ts.
236
+ const predicate = PredicateSchema.parse(aliasParam(args, 'predicate', ['until'])['predicate']);
222
237
  // Honesty: explicit since wins; else default to the last act's cursor; else the whole buffer.
223
238
  const since = asNumber(args['since']) ?? session.lastAct.cursor() ?? 0;
224
239
  const verdict = await waitForPredicate(session, predicate, asNumber(args['timeout_ms']) ?? DEFAULT_ASSERT_TIMEOUT_MS, since);
@@ -234,10 +249,11 @@ export const OBSERVE_TOOLS = [
234
249
  },
235
250
  {
236
251
  name: ReticleTool.ASSERT,
237
- example: { predicate: { kind: 'signal', name: 'todos:loaded' } },
252
+ example: { predicate: { kind: PredicateKind.SIGNAL, name: 'todos:loaded' } },
238
253
  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
254
  inputSchema: {
240
- predicate: PredicateSchema.describe('Predicate to evaluate: { signal }, { net }, { element } or a combination.'),
255
+ predicate: PredicateSchema.optional().describe('Predicate to evaluate: { signal }, { net }, { element } or a combination.'),
256
+ until: PredicateSchema.optional().describe("Alias for `predicate` (act_and_wait's name)."),
241
257
  timeout_ms: z
242
258
  .number()
243
259
  .optional()
@@ -287,7 +303,8 @@ export const OBSERVE_TOOLS = [
287
303
  },
288
304
  handler: async (deps, args) => {
289
305
  const session = deps.sessions.resolve(asString(args['sessionId']));
290
- const predicate = PredicateSchema.parse(args['predicate']);
306
+ // `until` is act_and_wait's name for this — see alias-args.ts.
307
+ const predicate = PredicateSchema.parse(aliasParam(args, 'predicate', ['until'])['predicate']);
291
308
  const timeout = asNumber(args['timeout_ms']) ?? 0;
292
309
  // Honesty: explicit since wins; else default to the last act's cursor; else the whole buffer.
293
310
  const since = asNumber(args['since']) ?? session.lastAct.cursor() ?? 0;
@@ -306,7 +323,7 @@ export const OBSERVE_TOOLS = [
306
323
  : assertsDerivedIpcStatus(predicate)
307
324
  ? { advice: DERIVED_IPC_STATUS_ADVICE }
308
325
  : {};
309
- const { decision, contradictions, coverage } = await assertVerdict(session, predicate, verdict.pass, since);
326
+ const { decision, contradictions, coverage } = await assertVerdict(session, predicate, verdict.pass, since, verdict.inconclusive);
310
327
  return withControl(session, {
311
328
  ...decision,
312
329
  ...verdict,
@@ -374,7 +391,7 @@ export const OBSERVE_TOOLS = [
374
391
  const method = asString(args['method']);
375
392
  const urlContains = asString(args['urlContains']);
376
393
  const status = asNumber(args['status']);
377
- const ok = typeof args['ok'] === 'boolean' ? args['ok'] : undefined;
394
+ const ok = 'boolean' === typeof args['ok'] ? args['ok'] : undefined;
378
395
  const limit = asNumber(args['limit']);
379
396
  const buffer = bufferEnvelope(session);
380
397
  // Completed calls + unresolved in-flight requests (a hung request shows as pending).
@@ -385,18 +402,21 @@ export const OBSERVE_TOOLS = [
385
402
  }));
386
403
  const matched = allNet.filter((e) => matchNet(e, method, urlContains, status, ok));
387
404
  // zero-match filter returns what DID fire, not a bare [].
388
- if (matched.length === 0 && allNet.length > 0) {
405
+ if (0 === matched.length && allNet.length > 0) {
389
406
  return withSizeCost({ calls: matched, hint: netEmptyHint(allNet), ...buffer });
390
407
  }
391
408
  // Default the cap so an omitted `limit` can't dump a whole flooded session (since defaults to 0).
392
409
  const { events: budgeted, droppedOldest } = applyEventBudget(matched, limit ?? DEFAULT_QUERY_LIMIT);
393
410
  const calls = budgeted.map(projectNetCall);
394
- return withSizeCost({
411
+ // A zero-match FILTER already reports what did fire (netEmptyHint above). Zero calls at all
412
+ // fell through as a bare `[]`, which is indistinguishable from an observer that is not
413
+ // recording — and those need opposite responses. Say the look happened.
414
+ return withSizeCost(noteEmptyRead({
395
415
  calls,
396
416
  ...(droppedOldest > 0 ? { total: matched.length, droppedOldest } : {}),
397
417
  ...bodiesNotCaptured(calls),
398
418
  ...buffer,
399
- });
419
+ }, 'calls', { noun: 'network calls' }));
400
420
  },
401
421
  },
402
422
  {
@@ -404,10 +424,9 @@ export const OBSERVE_TOOLS = [
404
424
  example: { level: 'error' },
405
425
  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
426
  inputSchema: {
407
- level: z
408
- .string()
427
+ level: consoleLevelEnum
409
428
  .optional()
410
- .describe('Log level filter: error | warn | info | log. Omit to return all levels.'),
429
+ .describe(`Log level filter: ${CONSOLE_LEVEL_LIST}. Omit to return all levels.`),
411
430
  since: z
412
431
  .number()
413
432
  .optional()
@@ -451,15 +470,19 @@ export const OBSERVE_TOOLS = [
451
470
  })).filter(isConsoleEvent);
452
471
  const matched = allConsole.filter((e) => matchConsole(e, level));
453
472
  // zero matches at this level → report what levels ARE present (not a bare []).
454
- if (matched.length === 0 && allConsole.length > 0) {
473
+ if (0 === matched.length && allConsole.length > 0) {
455
474
  return withSizeCost({ logs: matched, hint: consoleEmptyHint(allConsole), ...buffer });
456
475
  }
457
476
  // Default the cap so an omitted `limit` can't dump a whole flooded session (since defaults to 0).
458
477
  const { events: budgeted, droppedOldest } = applyEventBudget(matched, limit ?? DEFAULT_QUERY_LIMIT);
459
478
  const logs = budgeted.map(projectConsoleLog);
460
- return withSizeCost(droppedOldest > 0
479
+ // Say the look HAPPENED when it found nothing. A quiet page and a dead console observer both
480
+ // produced `{ logs: [] }`, and "no console errors" is the claim agents lean on most — see
481
+ // observed-nothing.ts, whose own header names this exact case and which every other array-
482
+ // returning read here was already wired to.
483
+ return withSizeCost(noteEmptyRead(droppedOldest > 0
461
484
  ? { logs, total: matched.length, droppedOldest, ...buffer }
462
- : { logs, ...buffer });
485
+ : { logs, ...buffer }, 'logs', { noun: 'console lines' }));
463
486
  },
464
487
  },
465
488
  {
@@ -469,6 +492,17 @@ export const OBSERVE_TOOLS = [
469
492
  outputSchema: {
470
493
  animations: z.array(z.unknown()),
471
494
  },
472
- handler: (deps, args) => commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.ANIMATIONS, {}),
495
+ handler: async (deps, args) => {
496
+ const result = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.ANIMATIONS, {});
497
+ // A page with nothing animating and an observer that is not watching both return `[]`, and the
498
+ // second means the run should not be trusted. Say the look happened.
499
+ return isPlainRecord(result)
500
+ ? noteEmptyRead(result, 'animations', { noun: 'animations running or completed' })
501
+ : result;
502
+ },
473
503
  },
474
504
  ];
505
+ /** Narrow a command result to something noteEmptyRead can annotate. */
506
+ function isPlainRecord(value) {
507
+ return 'object' === typeof value && value !== null && !Array.isArray(value);
508
+ }
@@ -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'],
@@ -270,8 +309,26 @@ export const READ_TOOLS = [
270
309
  store: z.string().optional(),
271
310
  path: z.string().optional(),
272
311
  availableKeys: z.array(z.string()).optional(),
312
+ // Present only when `availableKeys` is a SAMPLE. Declared for the same reason as the truncation
313
+ // report below: a capped list of 50 keys with no count reads as a complete one, so an agent
314
+ // concludes the key it wants is absent when it is merely past the cap.
315
+ totalKeys: z.number().optional(),
273
316
  component: z
274
- .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
+ })
275
332
  .optional(),
276
333
  // Truncation report — present ONLY when a transport cap trimmed the value. Declared so a
277
334
  // schema-strict client on the `full` profile KEEPS it: this is a false-green GUARD, and dropping
@@ -294,23 +351,31 @@ export const READ_TOOLS = [
294
351
  // Forward path/depth so a CURRENT browser SDK scopes the read IN-PAGE, before the transport —
295
352
  // the value never gets size-truncated in transit. (An older SDK ignores them and returns the
296
353
  // whole store; we then scope server-side below as a back-compat fallback.)
297
- const result = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.STATE_READ, {
354
+ const raw = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.STATE_READ, {
298
355
  ref: args['ref'],
299
356
  store,
300
357
  path,
301
358
  depth,
302
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;
303
368
  // Normalize storeNames to a string[] regardless of how the wire delivered it — the
304
369
  // outputSchema requires an array, and a non-array here makes MCP reject the whole result
305
370
  // (so the agent gets nothing instead of the state). Defensive: a string becomes a 1-element array.
306
371
  const root = result;
307
372
  const names = Array.isArray(root.storeNames)
308
- ? root.storeNames.filter((n) => typeof n === 'string')
309
- : 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
310
375
  ? [root.storeNames]
311
376
  : [];
312
377
  // The browser already scoped it in-page (the `found` shape) — pass through, just safe storeNames.
313
- if (typeof root.found === 'boolean') {
378
+ if ('boolean' === typeof root.found) {
314
379
  return { ...root, storeNames: names };
315
380
  }
316
381
  if (path === undefined && depth === undefined) {
@@ -368,7 +433,7 @@ export const READ_TOOLS = [
368
433
  // The walk stops at its node cap and returns a document-order prefix, so an inventory taken
369
434
  // from a big page is a floor, not a census — and this is the tool crawl's description points
370
435
  // agents at first for "a non-destructive list of what is here".
371
- ...(snap.truncated === true ? { truncated: true } : {}),
436
+ ...(true === snap.truncated ? { truncated: true } : {}),
372
437
  consoleErrors,
373
438
  hint: 'act on each ref, observe the reaction, and report failed requests / console errors / dead controls',
374
439
  // Buffer-honesty: the console-error count spans the whole buffer, which evicts — signal it.
@@ -408,13 +473,13 @@ export const READ_TOOLS = [
408
473
  const area = asString(args['area']);
409
474
  const key = asString(args['key']);
410
475
  const result = await commandOrThrow(deps, asString(args['sessionId']), ReticleCommand.STORAGE_READ, area !== undefined ? { area } : {});
411
- const data = (typeof result === 'object' && result !== null ? result : {});
476
+ const data = ('object' === typeof result && result !== null ? result : {});
412
477
  if (key === undefined)
413
478
  return data;
414
479
  // Look up the key in the scoped area, or across all three areas when none was named.
415
480
  const areas = area !== undefined ? [data] : [data['local'], data['session'], data['cookies']];
416
481
  for (const a of areas) {
417
- if (typeof a === 'object' && a !== null && key in a) {
482
+ if ('object' === typeof a && a !== null && key in a) {
418
483
  return { area, key, value: String(a[key]), found: true };
419
484
  }
420
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 {};