@reticlehq/server 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/SKILL.md +719 -0
  2. package/dist/bridge/auth-failure-reason.d.ts +19 -0
  3. package/dist/bridge/auth-failure-reason.js +29 -0
  4. package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
  5. package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
  6. package/dist/{bridge.d.ts → bridge/bridge.d.ts} +13 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +95 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
  10. package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
  11. package/dist/capsule/capsule-store.d.ts +8 -3
  12. package/dist/capsule/capsule-store.js +7 -2
  13. package/dist/capsule/capsule.js +3 -3
  14. package/dist/capsule/causal-summary.d.ts +8 -0
  15. package/dist/capsule/causal-summary.js +22 -12
  16. package/dist/capsule/divergence.d.ts +9 -4
  17. package/dist/capsule/divergence.js +8 -8
  18. package/dist/capsule/predicate-to-links.js +11 -8
  19. package/dist/cli/cli-doctor.d.ts +1 -0
  20. package/dist/cli/cli-doctor.js +135 -0
  21. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  22. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  23. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  24. package/dist/cli/cli-launch.js +171 -0
  25. package/dist/cli/cli-parse-feedback.d.ts +31 -0
  26. package/dist/cli/cli-parse-feedback.js +56 -0
  27. package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
  28. package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
  29. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
  30. package/dist/{cli-port.js → cli/cli-port.js} +9 -9
  31. package/dist/cli/cli-update-commands.d.ts +4 -0
  32. package/dist/cli/cli-update-commands.js +93 -0
  33. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  34. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  35. package/dist/cli/doctor-daemon-line.d.ts +37 -0
  36. package/dist/cli/doctor-daemon-line.js +35 -0
  37. package/dist/cli/port-holder.d.ts +43 -0
  38. package/dist/cli/port-holder.js +99 -0
  39. package/dist/cli.d.ts +2 -2
  40. package/dist/cli.js +302 -100
  41. package/dist/cloud/cloud-config.d.ts +0 -1
  42. package/dist/cloud/cloud-config.js +9 -9
  43. package/dist/cloud/cloud-sync.d.ts +1 -7
  44. package/dist/cloud/cloud-sync.js +12 -12
  45. package/dist/crawl/crawl-empty.d.ts +16 -0
  46. package/dist/crawl/crawl-empty.js +24 -0
  47. package/dist/crawl/crawl-tools.js +4 -1
  48. package/dist/crawl/crawl.js +26 -7
  49. package/dist/daemon/daemon-resilience.d.ts +73 -0
  50. package/dist/daemon/daemon-resilience.js +279 -0
  51. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  52. package/dist/daemon/daemon-usefulness.js +62 -0
  53. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  54. package/dist/{daemon.js → daemon/daemon.js} +105 -48
  55. package/dist/daemon/heartbeat.d.ts +115 -0
  56. package/dist/daemon/heartbeat.js +141 -0
  57. package/dist/daemon/idle-grace.d.ts +32 -0
  58. package/dist/daemon/idle-grace.js +41 -0
  59. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +23 -2
  60. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
  61. package/dist/daemon/port-presence.d.ts +76 -0
  62. package/dist/daemon/port-presence.js +93 -0
  63. package/dist/daemon/state-dir.d.ts +7 -0
  64. package/dist/daemon/state-dir.js +59 -0
  65. package/dist/daemon/unclean-predecessor.d.ts +23 -0
  66. package/dist/daemon/unclean-predecessor.js +27 -0
  67. package/dist/domain/domain-model.js +2 -2
  68. package/dist/events/body-failures.js +8 -8
  69. package/dist/events/contradictions.d.ts +1 -1
  70. package/dist/events/contradictions.js +60 -10
  71. package/dist/events/echo-mismatch.js +10 -10
  72. package/dist/events/event-filters.js +6 -6
  73. package/dist/events/json-salvage.d.ts +2 -1
  74. package/dist/events/json-salvage.js +6 -6
  75. package/dist/events/observed-in-window.d.ts +12 -0
  76. package/dist/events/observed-in-window.js +21 -0
  77. package/dist/events/predicate-eval.d.ts +53 -20
  78. package/dist/events/predicate-eval.js +179 -55
  79. package/dist/events/predicate-parse.d.ts +23 -0
  80. package/dist/events/predicate-parse.js +54 -0
  81. package/dist/events/predicate.d.ts +7 -2
  82. package/dist/events/predicate.js +126 -35
  83. package/dist/events/reconcile.js +5 -5
  84. package/dist/events/ring-buffer.js +2 -17
  85. package/dist/events/stale-response.js +6 -6
  86. package/dist/events/testid-near-miss.d.ts +22 -0
  87. package/dist/events/testid-near-miss.js +33 -0
  88. package/dist/events/unit-mismatch.js +8 -8
  89. package/dist/flows/affected-tools.js +19 -4
  90. package/dist/flows/affected.js +1 -1
  91. package/dist/flows/annotate-target.d.ts +1 -0
  92. package/dist/flows/annotate-target.js +20 -0
  93. package/dist/flows/annotate-tools.js +25 -3
  94. package/dist/flows/annotate.js +17 -4
  95. package/dist/flows/assertion-integrity.d.ts +2 -1
  96. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  97. package/dist/flows/assertion-tiers-store.js +2 -2
  98. package/dist/flows/attributed-failure.d.ts +17 -0
  99. package/dist/flows/attributed-failure.js +20 -0
  100. package/dist/flows/buddy-status.d.ts +2 -1
  101. package/dist/flows/buddy-status.js +2 -2
  102. package/dist/flows/coverage.d.ts +3 -2
  103. package/dist/flows/coverage.js +2 -2
  104. package/dist/flows/decision.js +24 -9
  105. package/dist/flows/empty-flow.d.ts +6 -0
  106. package/dist/flows/empty-flow.js +24 -0
  107. package/dist/flows/flake-store.d.ts +0 -1
  108. package/dist/flows/flake-store.js +1 -1
  109. package/dist/flows/flake.d.ts +2 -1
  110. package/dist/flows/flake.js +2 -2
  111. package/dist/flows/flow-classify.js +12 -0
  112. package/dist/flows/flow-replay-run.js +4 -4
  113. package/dist/flows/flow-replay.d.ts +6 -8
  114. package/dist/flows/flow-replay.js +132 -48
  115. package/dist/flows/flow-report.js +1 -1
  116. package/dist/flows/flow-scope.d.ts +1 -6
  117. package/dist/flows/flow-step-runners.d.ts +26 -6
  118. package/dist/flows/flow-step-runners.js +156 -6
  119. package/dist/flows/flow-success.js +20 -15
  120. package/dist/flows/flow-tools.js +63 -21
  121. package/dist/flows/flows.js +15 -11
  122. package/dist/flows/gate.d.ts +4 -3
  123. package/dist/flows/gate.js +1 -1
  124. package/dist/flows/heal-run.d.ts +0 -8
  125. package/dist/flows/heal-run.js +5 -5
  126. package/dist/flows/heal.js +3 -3
  127. package/dist/flows/predicate-to-expect.d.ts +38 -0
  128. package/dist/flows/predicate-to-expect.js +95 -0
  129. package/dist/flows/replay.d.ts +16 -1
  130. package/dist/flows/replay.js +58 -22
  131. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  132. package/dist/flows/role-anchor-nearest.js +45 -0
  133. package/dist/flows/role-drift-reason.d.ts +20 -0
  134. package/dist/flows/role-drift-reason.js +39 -0
  135. package/dist/flows/server-verify.d.ts +1 -1
  136. package/dist/flows/server-verify.js +5 -5
  137. package/dist/flows/suite-flakes.d.ts +33 -0
  138. package/dist/flows/suite-flakes.js +32 -0
  139. package/dist/flows/verify-change-tools.js +32 -10
  140. package/dist/flows/verify-change-verdict.d.ts +15 -0
  141. package/dist/flows/verify-change-verdict.js +25 -0
  142. package/dist/flows/watch-batcher.d.ts +3 -2
  143. package/dist/honesty/already-true.d.ts +2 -0
  144. package/dist/honesty/already-true.js +31 -0
  145. package/dist/honesty/blind-spots.d.ts +35 -3
  146. package/dist/honesty/blind-spots.js +57 -12
  147. package/dist/honesty/honesty.d.ts +3 -2
  148. package/dist/honesty/honesty.js +4 -4
  149. package/dist/honesty/uncaptured-bodies.js +1 -1
  150. package/dist/honesty/unread-outcome.js +2 -2
  151. package/dist/honesty/verified.d.ts +31 -3
  152. package/dist/honesty/verified.js +64 -8
  153. package/dist/http-server.js +23 -7
  154. package/dist/hunt/hunt-report.d.ts +2 -1
  155. package/dist/hunt/hunt-report.js +2 -2
  156. package/dist/index.d.ts +9 -6
  157. package/dist/index.js +41 -25
  158. package/dist/init/agent-rules.d.ts +20 -1
  159. package/dist/init/agent-rules.js +64 -8
  160. package/dist/init/app-choice.d.ts +20 -0
  161. package/dist/init/app-choice.js +25 -0
  162. package/dist/init/astro-host.d.ts +21 -0
  163. package/dist/init/astro-host.js +27 -0
  164. package/dist/init/astro-patch.d.ts +16 -0
  165. package/dist/init/astro-patch.js +240 -0
  166. package/dist/init/capabilities.js +4 -1
  167. package/dist/init/cra.d.ts +50 -0
  168. package/dist/init/cra.js +106 -0
  169. package/dist/init/cursor.js +39 -4
  170. package/dist/init/desktop-doctor.d.ts +3 -2
  171. package/dist/init/desktop-doctor.js +4 -4
  172. package/dist/init/detect.d.ts +9 -2
  173. package/dist/init/detect.js +24 -5
  174. package/dist/init/mcp-clients.d.ts +81 -0
  175. package/dist/init/mcp-clients.js +248 -0
  176. package/dist/init/mcp-registered.d.ts +12 -0
  177. package/dist/init/mcp-registered.js +14 -0
  178. package/dist/init/mcp.d.ts +27 -2
  179. package/dist/init/mcp.js +40 -4
  180. package/dist/init/next-patch.d.ts +2 -4
  181. package/dist/init/next-patch.js +109 -19
  182. package/dist/init/node-io.js +29 -6
  183. package/dist/init/plan-framework.d.ts +16 -1
  184. package/dist/init/plan-framework.js +166 -8
  185. package/dist/init/plan.d.ts +85 -6
  186. package/dist/init/plan.js +196 -33
  187. package/dist/init/project-id.d.ts +5 -9
  188. package/dist/init/project-id.js +10 -22
  189. package/dist/init/run.d.ts +23 -1
  190. package/dist/init/run.js +338 -47
  191. package/dist/init/slash-command.d.ts +9 -0
  192. package/dist/init/slash-command.js +10 -1
  193. package/dist/init/snippets.d.ts +7 -10
  194. package/dist/init/snippets.js +44 -15
  195. package/dist/init/vite-config.js +1 -1
  196. package/dist/init/windows-quote.d.ts +38 -0
  197. package/dist/init/windows-quote.js +70 -0
  198. package/dist/init/workspace-apps.d.ts +23 -0
  199. package/dist/init/workspace-apps.js +68 -0
  200. package/dist/input/driven-redaction.js +1 -1
  201. package/dist/input/network-detail.js +8 -8
  202. package/dist/input/network-mock-tools.js +2 -2
  203. package/dist/input/network-mock.js +4 -4
  204. package/dist/input/real-input.js +8 -3
  205. package/dist/input/scroll-find.js +2 -2
  206. package/dist/input/scroll-tools.d.ts +0 -5
  207. package/dist/input/scroll-tools.js +11 -4
  208. package/dist/input/viewport-tools.js +2 -2
  209. package/dist/journal/ambient-store.d.ts +0 -2
  210. package/dist/journal/ambient-store.js +1 -1
  211. package/dist/journal/ambient.js +1 -1
  212. package/dist/journal/attach-journal.d.ts +2 -1
  213. package/dist/journal/deviation-report.js +6 -6
  214. package/dist/journal/deviation-service.js +1 -1
  215. package/dist/journal/envelope-store.d.ts +0 -2
  216. package/dist/journal/envelope-store.js +1 -1
  217. package/dist/journal/envelope.d.ts +2 -3
  218. package/dist/journal/envelope.js +3 -3
  219. package/dist/journal/journal-query.js +1 -1
  220. package/dist/journal/journal-recorder.d.ts +2 -1
  221. package/dist/journal/journal-recorder.js +2 -2
  222. package/dist/journal/rollups.js +9 -9
  223. package/dist/journal/session-end.d.ts +2 -1
  224. package/dist/journal/session-journal.js +4 -4
  225. package/dist/license/license.d.ts +0 -14
  226. package/dist/license/license.js +8 -8
  227. package/dist/log.d.ts +10 -0
  228. package/dist/log.js +11 -1
  229. package/dist/mcp/mcp-is-error.d.ts +13 -0
  230. package/dist/mcp/mcp-is-error.js +18 -0
  231. package/dist/mcp/mcp-outage.d.ts +32 -0
  232. package/dist/mcp/mcp-outage.js +55 -0
  233. package/dist/mcp/mcp-proxy.d.ts +134 -0
  234. package/dist/mcp/mcp-proxy.js +720 -0
  235. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  236. package/dist/{mcp.js → mcp/mcp.js} +139 -37
  237. package/dist/mcp/proxy-handshake.d.ts +3 -0
  238. package/dist/mcp/proxy-handshake.js +54 -0
  239. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  240. package/dist/mcp/proxy-lifecycle.js +70 -0
  241. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  242. package/dist/mcp/tool-catalog-cache.js +55 -0
  243. package/dist/oracles/propose-consequences.d.ts +2 -1
  244. package/dist/oracles/propose-consequences.js +16 -11
  245. package/dist/oracles/self-instrument.d.ts +2 -1
  246. package/dist/oracles/self-instrument.js +1 -1
  247. package/dist/phenomena/phenomena.js +3 -3
  248. package/dist/platform.d.ts +16 -0
  249. package/dist/platform.js +16 -0
  250. package/dist/pool/browser-pool.d.ts +9 -0
  251. package/dist/pool/browser-pool.js +38 -9
  252. package/dist/pool/playwright-launcher.d.ts +15 -2
  253. package/dist/pool/playwright-launcher.js +21 -2
  254. package/dist/project/fs-port.js +1 -1
  255. package/dist/project/project-tools.js +2 -2
  256. package/dist/runs/build-verification-run.js +1 -1
  257. package/dist/runs/repair-prompt.d.ts +0 -6
  258. package/dist/runs/repair-prompt.js +3 -3
  259. package/dist/runs/risk-classify.js +1 -1
  260. package/dist/runs/run-diff.d.ts +0 -2
  261. package/dist/runs/run-diff.js +3 -3
  262. package/dist/runs/run-tools.js +2 -2
  263. package/dist/runs/verification-sync.js +2 -2
  264. package/dist/runs/verify-http.js +1 -1
  265. package/dist/runs/verify-server.js +1 -1
  266. package/dist/session/attachment-history.d.ts +46 -0
  267. package/dist/session/attachment-history.js +65 -0
  268. package/dist/session/command-timeout.d.ts +11 -3
  269. package/dist/session/command-timeout.js +2 -2
  270. package/dist/session/control-envelope.d.ts +6 -0
  271. package/dist/session/control-envelope.js +7 -1
  272. package/dist/session/dev-server-probe.d.ts +13 -0
  273. package/dist/session/dev-server-probe.js +52 -0
  274. package/dist/session/last-act.d.ts +17 -3
  275. package/dist/session/last-act.js +16 -4
  276. package/dist/session/live-control-tools.js +7 -2
  277. package/dist/session/live-control.js +1 -1
  278. package/dist/session/looks-like-dev-server.d.ts +14 -0
  279. package/dist/session/looks-like-dev-server.js +25 -0
  280. package/dist/session/no-session-diagnosis.d.ts +45 -0
  281. package/dist/session/no-session-diagnosis.js +120 -0
  282. package/dist/session/no-session-watch.d.ts +21 -0
  283. package/dist/session/no-session-watch.js +69 -0
  284. package/dist/session/observed-state.d.ts +4 -0
  285. package/dist/session/observed-state.js +19 -1
  286. package/dist/session/output-budget.js +1 -1
  287. package/dist/session/pending-commands.js +1 -0
  288. package/dist/session/session-health.d.ts +8 -1
  289. package/dist/session/session-health.js +8 -7
  290. package/dist/session/session-info.d.ts +43 -8
  291. package/dist/session/session-info.js +46 -1
  292. package/dist/session/session-lease.d.ts +0 -11
  293. package/dist/session/session-lease.js +1 -1
  294. package/dist/session/session-manager.d.ts +6 -0
  295. package/dist/session/session-manager.js +124 -12
  296. package/dist/session/session-readiness.d.ts +1 -1
  297. package/dist/session/session-readiness.js +6 -1
  298. package/dist/session/session-reaper.js +1 -1
  299. package/dist/session/session-reconnect.d.ts +32 -0
  300. package/dist/session/session-reconnect.js +42 -0
  301. package/dist/session/session.d.ts +15 -2
  302. package/dist/session/session.js +77 -26
  303. package/dist/session/state-select.d.ts +1 -1
  304. package/dist/session/state-select.js +1 -1
  305. package/dist/status-payload.d.ts +19 -0
  306. package/dist/status-payload.js +11 -0
  307. package/dist/telemetry/argument-shape.js +10 -1
  308. package/dist/telemetry/browser-mode.d.ts +27 -0
  309. package/dist/telemetry/browser-mode.js +33 -0
  310. package/dist/telemetry/bug-found.d.ts +17 -21
  311. package/dist/telemetry/bug-found.js +120 -8
  312. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  313. package/dist/telemetry/cli-telemetry.js +27 -4
  314. package/dist/telemetry/crash-cause.d.ts +47 -0
  315. package/dist/telemetry/crash-cause.js +104 -0
  316. package/dist/telemetry/daemon-telemetry.d.ts +38 -1
  317. package/dist/telemetry/daemon-telemetry.js +37 -12
  318. package/dist/telemetry/dev-repo.js +4 -4
  319. package/dist/telemetry/error-class.d.ts +26 -0
  320. package/dist/telemetry/error-class.js +52 -0
  321. package/dist/telemetry/error-fingerprint.js +21 -2
  322. package/dist/telemetry/feedback-cli.d.ts +8 -3
  323. package/dist/telemetry/feedback-cli.js +52 -8
  324. package/dist/telemetry/feedback-context.d.ts +23 -6
  325. package/dist/telemetry/feedback-context.js +105 -5
  326. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  327. package/dist/telemetry/feedback-delivery.js +30 -0
  328. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  329. package/dist/telemetry/feedback-outbox.js +87 -0
  330. package/dist/telemetry/feedback.d.ts +22 -1
  331. package/dist/telemetry/feedback.js +67 -8
  332. package/dist/telemetry/git-facts.d.ts +7 -0
  333. package/dist/telemetry/git-facts.js +5 -5
  334. package/dist/telemetry/project-profile.js +2 -1
  335. package/dist/telemetry/session-metrics.d.ts +47 -3
  336. package/dist/telemetry/session-metrics.js +315 -15
  337. package/dist/telemetry/telemetry.d.ts +45 -2
  338. package/dist/telemetry/telemetry.js +150 -37
  339. package/dist/telemetry/verification-of.d.ts +22 -0
  340. package/dist/telemetry/verification-of.js +84 -0
  341. package/dist/tools/act-capsule.js +2 -2
  342. package/dist/tools/act-danger.d.ts +12 -0
  343. package/dist/tools/act-danger.js +20 -3
  344. package/dist/tools/act-helpers.js +4 -4
  345. package/dist/tools/act-tools.d.ts +0 -8
  346. package/dist/tools/act-tools.js +114 -118
  347. package/dist/tools/act-view.js +7 -7
  348. package/dist/tools/alias-args.d.ts +17 -0
  349. package/dist/tools/alias-args.js +25 -0
  350. package/dist/tools/assert-grade.js +9 -9
  351. package/dist/tools/assert-verdict.d.ts +9 -1
  352. package/dist/tools/assert-verdict.js +21 -6
  353. package/dist/tools/browser-tools.js +36 -11
  354. package/dist/tools/contract-tools.d.ts +0 -5
  355. package/dist/tools/contract-tools.js +27 -1
  356. package/dist/tools/coverage-identity.d.ts +34 -0
  357. package/dist/tools/coverage-identity.js +46 -0
  358. package/dist/tools/coverage-tools.js +10 -14
  359. package/dist/tools/dynamic-tools.d.ts +2 -1
  360. package/dist/tools/dynamic-tools.js +96 -19
  361. package/dist/tools/error-recovery.d.ts +34 -2
  362. package/dist/tools/error-recovery.js +255 -4
  363. package/dist/tools/feedback-invite.d.ts +48 -0
  364. package/dist/tools/feedback-invite.js +71 -0
  365. package/dist/tools/feedback-tools.d.ts +12 -1
  366. package/dist/tools/feedback-tools.js +28 -5
  367. package/dist/tools/invoke-tool.d.ts +0 -12
  368. package/dist/tools/invoke-tool.js +126 -33
  369. package/dist/tools/lease-hint.d.ts +17 -0
  370. package/dist/tools/lease-hint.js +24 -0
  371. package/dist/tools/lease-tools.js +6 -5
  372. package/dist/tools/merge-tools.d.ts +10 -1
  373. package/dist/tools/merge-tools.js +9 -3
  374. package/dist/tools/merged-name-redirect.d.ts +13 -0
  375. package/dist/tools/merged-name-redirect.js +55 -0
  376. package/dist/tools/navigate-arrival.d.ts +26 -0
  377. package/dist/tools/navigate-arrival.js +60 -0
  378. package/dist/tools/navigate-result.d.ts +31 -0
  379. package/dist/tools/navigate-result.js +38 -0
  380. package/dist/tools/observe-tools.js +72 -20
  381. package/dist/tools/observed-nothing.d.ts +25 -0
  382. package/dist/tools/observed-nothing.js +33 -0
  383. package/dist/tools/query-paginate.js +2 -2
  384. package/dist/tools/query-shape.d.ts +18 -0
  385. package/dist/tools/query-shape.js +39 -0
  386. package/dist/tools/read-tools.js +81 -20
  387. package/dist/tools/real-input-attempt.d.ts +28 -0
  388. package/dist/tools/real-input-attempt.js +93 -0
  389. package/dist/tools/reconcile-tools.js +7 -7
  390. package/dist/tools/reload-result.d.ts +14 -0
  391. package/dist/tools/reload-result.js +29 -0
  392. package/dist/tools/snapshot-delta.d.ts +4 -5
  393. package/dist/tools/snapshot-delta.js +0 -0
  394. package/dist/tools/tool-kit.d.ts +16 -0
  395. package/dist/tools/tool-surface.d.ts +83 -0
  396. package/dist/tools/tool-surface.js +217 -0
  397. package/dist/tools/tools-helpers.js +6 -6
  398. package/dist/tools/tools.d.ts +15 -0
  399. package/dist/tools/tools.js +53 -17
  400. package/dist/tools/unadvertised-help.d.ts +5 -0
  401. package/dist/tools/unadvertised-help.js +39 -0
  402. package/dist/trace.d.ts +69 -0
  403. package/dist/trace.js +150 -0
  404. package/dist/update/nudge-credit.d.ts +4 -0
  405. package/dist/update/nudge-credit.js +53 -0
  406. package/dist/update/sdk-sync.d.ts +21 -0
  407. package/dist/update/sdk-sync.js +42 -0
  408. package/dist/update/update-checker.js +3 -3
  409. package/dist/update/update-nudge.d.ts +63 -6
  410. package/dist/update/update-nudge.js +128 -15
  411. package/dist/update/updater.d.ts +1 -9
  412. package/dist/update/updater.js +13 -8
  413. package/dist/version/peer-announce.d.ts +4 -0
  414. package/dist/version/peer-announce.js +30 -0
  415. package/dist/{server-version.js → version/server-version.js} +5 -1
  416. package/dist/version/version-nudge.d.ts +28 -0
  417. package/dist/version/version-nudge.js +40 -0
  418. package/dist/version/version-skew.d.ts +66 -0
  419. package/dist/version/version-skew.js +71 -0
  420. package/dist/visual/visual-diff.js +2 -2
  421. package/dist/visual/visual-tools.js +4 -4
  422. package/docs/README.md +55 -0
  423. package/docs/agent-cheatsheet.md +85 -0
  424. package/docs/architecture.md +113 -0
  425. package/docs/benchmarks.md +255 -0
  426. package/docs/debugging.md +102 -0
  427. package/docs/deploy-checks.md +69 -0
  428. package/docs/desktop-apps.md +256 -0
  429. package/docs/enterprise.md +62 -0
  430. package/docs/fixtures-dispatch-receiver.yml +76 -0
  431. package/docs/fixtures.md +45 -0
  432. package/docs/flows.md +166 -0
  433. package/docs/gate-plan.md +98 -0
  434. package/docs/gates.md +117 -0
  435. package/docs/getting-started.md +469 -0
  436. package/docs/human-control.md +63 -0
  437. package/docs/integration-patterns.md +171 -0
  438. package/docs/local-registry.md +126 -0
  439. package/docs/matrix/2.5.0/claude-code-machine.json +19 -0
  440. package/docs/matrix/2.5.0/codex-machine.json +20 -0
  441. package/docs/matrix/2.5.0/cursor-machine.json +22 -0
  442. package/docs/matrix/2.5.0/gemini-machine.json +22 -0
  443. package/docs/matrix/2.5.0/opencode-machine.json +22 -0
  444. package/docs/matrix/2.5.0/vscode-machine.json +22 -0
  445. package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
  446. package/docs/matrix/MATRIX.md +26 -0
  447. package/docs/matrix/README.md +59 -0
  448. package/docs/mint.json +61 -0
  449. package/docs/multi-agent-testing.md +55 -0
  450. package/docs/platform-integration.md +122 -0
  451. package/docs/system-map.md +152 -0
  452. package/docs/telemetry-contract.md +214 -0
  453. package/docs/telemetry.md +191 -0
  454. package/docs/testing.md +82 -0
  455. package/docs/token-efficiency.md +80 -0
  456. package/docs/usage.md +1046 -0
  457. package/package.json +8 -5
  458. package/dist/cli-doctor.d.ts +0 -6
  459. package/dist/cli-doctor.js +0 -60
  460. package/dist/cli-launch.js +0 -112
  461. package/dist/daemon-resilience.d.ts +0 -22
  462. package/dist/daemon-resilience.js +0 -85
  463. package/dist/mcp-proxy.d.ts +0 -67
  464. package/dist/mcp-proxy.js +0 -337
  465. package/dist/tools/profiles.d.ts +0 -37
  466. package/dist/tools/profiles.js +0 -139
  467. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  468. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  469. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  470. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  471. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  472. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -1,84 +1,153 @@
1
- import { ElementQuerySchema, ElementState, EventType, } from '@reticlehq/core';
1
+ import { ElementQuerySchema, ElementState, EventType, PredicateKind, StreamDirection, isDevToolingUrl, } from '@reticlehq/core';
2
2
  import { z } from 'zod';
3
- export const PredicateSchema = z.lazy(() => z.discriminatedUnion('kind', [
4
- z.object({
5
- kind: z.literal('element'),
3
+ import { describeObserved } from './observed-in-window.js';
4
+ /**
5
+ * Spellings an agent plausibly reaches for, mapped to the real field.
6
+ *
7
+ * `route` spells its field `pathname` while `state`, in the same union, spells its `path`. Every one
8
+ * of these was silently DROPPED before, and because these kinds have all-optional fields, dropping
9
+ * the only key supplied left a predicate that asserts nothing and passes on anything.
10
+ */
11
+ const PREDICATE_ALIASES = {
12
+ // `urlContains`/`url` reported from the field: an agent that had just written
13
+ // `net { urlContains }` applied the same word to `route`, which spells it `contains`, and got
14
+ // `unrecognized_keys` with no list of what would have worked. The parallel it assumed is a fair
15
+ // one — route's `contains` matches the WHOLE route (path + query + fragment), so "the URL
16
+ // contains this" is precisely what it does — and asserting a redirect after login is the single
17
+ // most common thing an agent reaches for here.
18
+ [PredicateKind.ROUTE]: { path: 'pathname', urlContains: 'contains', url: 'contains' },
19
+ [PredicateKind.NET]: { url: 'urlContains' },
20
+ [PredicateKind.SIGNAL]: { data: 'dataMatches' },
21
+ };
22
+ /** Rename known aliases before parse; an explicit canonical key always wins. */
23
+ function applyPredicateAliases(input) {
24
+ if (typeof input !== 'object' || null === input || Array.isArray(input))
25
+ return input;
26
+ const obj = input;
27
+ const aliases = PREDICATE_ALIASES['string' === typeof obj['kind'] ? obj['kind'] : ''];
28
+ if (aliases === undefined)
29
+ return input;
30
+ const out = { ...obj };
31
+ for (const [from, to] of Object.entries(aliases)) {
32
+ if (out[from] === undefined)
33
+ continue;
34
+ if (out[to] === undefined)
35
+ out[to] = out[from];
36
+ delete out[from];
37
+ }
38
+ return out;
39
+ }
40
+ /**
41
+ * Strict on every branch. A key that is nobody's spelling is now a schema error naming it, instead of
42
+ * a stripped field and a green — see predicate-strict.test.ts for the MCP session that found this.
43
+ */
44
+ export const PredicateSchema = z.lazy(() => z.preprocess(applyPredicateAliases, z.discriminatedUnion('kind', [
45
+ z
46
+ .object({
47
+ kind: z.literal(PredicateKind.ELEMENT),
6
48
  query: ElementQuerySchema,
7
49
  state: z.nativeEnum(ElementState).optional(),
8
50
  absent: z.boolean().optional(),
9
- }),
10
- z.object({
11
- kind: z.literal('text'),
51
+ })
52
+ .strict(),
53
+ z
54
+ .object({
55
+ kind: z.literal(PredicateKind.TEXT),
12
56
  contains: z.string(),
13
57
  visible: z.boolean().optional(),
14
58
  absent: z.boolean().optional(),
15
- }),
16
- z.object({
17
- kind: z.literal('net'),
59
+ })
60
+ .strict(),
61
+ z
62
+ .object({
63
+ kind: z.literal(PredicateKind.NET),
18
64
  method: z.string().optional(),
19
65
  urlContains: z.string().optional(),
20
66
  status: z.number().optional(),
21
67
  ok: z.boolean().optional(),
22
68
  since: z.number().optional(),
23
69
  count: z.number().int().nonnegative().optional(),
24
- }),
25
- z.object({
26
- kind: z.literal('route'),
70
+ })
71
+ .strict(),
72
+ z
73
+ .object({
74
+ kind: z.literal(PredicateKind.ROUTE),
27
75
  pathname: z.string().optional(),
28
76
  contains: z.string().optional(),
29
- }),
30
- z.object({
31
- kind: z.literal('console'),
77
+ since: z.number().optional(),
78
+ })
79
+ .strict(),
80
+ z
81
+ .object({
82
+ kind: z.literal(PredicateKind.CONSOLE),
32
83
  level: z.string().optional(),
33
84
  absent: z.boolean().optional(),
34
85
  since: z.number().optional(),
35
- }),
36
- z.object({
37
- kind: z.literal('animation'),
86
+ })
87
+ .strict(),
88
+ z
89
+ .object({
90
+ kind: z.literal(PredicateKind.ANIMATION),
38
91
  name: z.string().optional(),
39
92
  target: z.string().optional(),
40
93
  completed: z.boolean().optional(),
41
- }),
42
- z.object({
43
- kind: z.literal('signal'),
94
+ since: z.number().optional(),
95
+ })
96
+ .strict(),
97
+ z
98
+ .object({
99
+ kind: z.literal(PredicateKind.SIGNAL),
44
100
  name: z.string().optional(),
45
101
  dataMatches: z.record(z.unknown()).optional(),
46
- }),
47
- z.object({
48
- kind: z.literal('state'),
102
+ since: z.number().optional(),
103
+ })
104
+ .strict(),
105
+ z
106
+ .object({
107
+ kind: z.literal(PredicateKind.STATE),
49
108
  store: z.string().optional(),
50
109
  path: z.string(),
51
110
  equals: z.unknown().optional(),
52
- }),
53
- z.object({ kind: z.literal('settled'), quietMs: z.number().positive().optional() }),
54
- z.object({ kind: z.literal('allOf'), predicates: z.array(PredicateSchema) }),
55
- z.object({ kind: z.literal('anyOf'), predicates: z.array(PredicateSchema) }),
56
- z.object({ kind: z.literal('not'), predicate: PredicateSchema }),
57
- ]));
111
+ })
112
+ .strict(),
113
+ z
114
+ .object({
115
+ kind: z.literal(PredicateKind.SETTLED),
116
+ quietMs: z.number().positive().optional(),
117
+ })
118
+ .strict(),
119
+ z
120
+ .object({ kind: z.literal(PredicateKind.ALL_OF), predicates: z.array(PredicateSchema) })
121
+ .strict(),
122
+ z
123
+ .object({ kind: z.literal(PredicateKind.ANY_OF), predicates: z.array(PredicateSchema) })
124
+ .strict(),
125
+ z.object({ kind: z.literal(PredicateKind.NOT), predicate: PredicateSchema }).strict(),
126
+ ])));
58
127
  function str(value) {
59
- return typeof value === 'string' ? value : undefined;
128
+ return 'string' === typeof value ? value : undefined;
60
129
  }
61
130
  function num(value) {
62
- return typeof value === 'number' ? value : undefined;
131
+ return 'number' === typeof value ? value : undefined;
63
132
  }
64
133
  /**
65
134
  * Match one value against a pattern. Supports `*` (present), strict equality, and operators:
66
135
  * `{$gte,$lte,$gt,$lt}` (numbers), `{$contains}` (array membership or substring), `{$length}`.
67
136
  */
68
137
  export function matchValue(got, want) {
69
- if (want === '*')
138
+ if ('*' === want)
70
139
  return got !== undefined;
71
140
  // An object is an OPERATOR container only if it actually carries a `$`-prefixed operator. An empty
72
141
  // `{}` (or an object with no `$` key) used to enter this branch, iterate zero recognized operators,
73
142
  // and `return true` — so `equals: {}` / `dataMatches: {status: {}}` was a green assertion that
74
143
  // passed against ANYTHING, undefined included: the exact false green the oracle exists to catch.
75
144
  // Without an operator it is a literal to compare, and falls through to strict equality below.
76
- const ops = typeof want === 'object' && want !== null && !Array.isArray(want)
145
+ const ops = 'object' === typeof want && want !== null && !Array.isArray(want)
77
146
  ? Object.entries(want)
78
147
  : undefined;
79
148
  if (ops !== undefined && ops.some(([op]) => op.startsWith('$'))) {
80
149
  for (const [op, val] of ops) {
81
- const n = typeof got === 'number' ? got : NaN;
150
+ const n = 'number' === typeof got ? got : NaN;
82
151
  switch (op) {
83
152
  case '$gte':
84
153
  if (!(n >= val))
@@ -101,7 +170,7 @@ export function matchValue(got, want) {
101
170
  if (!got.includes(val))
102
171
  return false;
103
172
  }
104
- else if (typeof got === 'string') {
173
+ else if ('string' === typeof got) {
105
174
  if (!got.includes(String(val)))
106
175
  return false;
107
176
  }
@@ -110,7 +179,7 @@ export function matchValue(got, want) {
110
179
  }
111
180
  break;
112
181
  case '$length':
113
- if (!((Array.isArray(got) || typeof got === 'string') && got.length === val)) {
182
+ if (!((Array.isArray(got) || 'string' === typeof got) && got.length === val)) {
114
183
  return false;
115
184
  }
116
185
  break;
@@ -140,11 +209,27 @@ function dataMatches(actual, pattern) {
140
209
  * This exists so an agent can assert on the OUTCOME rather than on a number Reticle invented.
141
210
  */
142
211
  function callSucceeded(data) {
143
- if (typeof data['ok'] === 'boolean')
212
+ if ('boolean' === typeof data['ok'])
144
213
  return data['ok'];
145
214
  const status = num(data['status']);
146
215
  return status === undefined || status < 400;
147
216
  }
217
+ /**
218
+ * One call, as the failure report should name it: `POST /api/generate-script → 500`.
219
+ *
220
+ * The status used to be dropped here, which made the most common net failure unreadable. Asserting
221
+ * `{status: 200}` against a call that returned 500 reported only `POST /api/generate-script` — the
222
+ * very field the predicate filtered on was missing from the account of what was seen, so the agent
223
+ * is told the call happened and left to guess why it did not match.
224
+ *
225
+ * The arrow is OMITTED when there is no status. An in-flight or aborted request genuinely has none,
226
+ * and `→ undefined` would be a fabricated fact about the wire.
227
+ */
228
+ function describeCall(e) {
229
+ const head = `${str(e.data['method']) ?? 'GET'} ${str(e.data['url']) ?? ''}`;
230
+ const status = num(e.data['status']);
231
+ return status === undefined ? head : `${head} → ${String(status)}`;
232
+ }
148
233
  export function evalNet(events, p) {
149
234
  const since = p.since ?? 0;
150
235
  const matches = events.filter((e) => {
@@ -183,7 +268,9 @@ export function evalNet(events, p) {
183
268
  : {
184
269
  pass: false,
185
270
  failureReason: `no network call matched ${JSON.stringify(p)}`,
186
- observed: 'no matching network call in the window',
271
+ // Same reasoning as the signal miss: "no matching call" cannot be told apart from "the app
272
+ // made no calls at all", and those need different fixes.
273
+ observed: describeObserved('calls', events.filter((e) => e.type === EventType.NET_REQUEST).map(describeCall)),
187
274
  expected: `at least one call matching ${JSON.stringify(p)}`,
188
275
  assertion: 'net.present',
189
276
  };
@@ -257,17 +344,17 @@ export function evalConsole(events, p) {
257
344
  e.type === EventType.CONSOLE_ERROR ||
258
345
  e.type === EventType.ERROR_UNCAUGHT);
259
346
  }
260
- if (p.level === 'error')
347
+ if ('error' === p.level)
261
348
  return isErr;
262
349
  return e.type === CONSOLE_LEVEL_TYPE[p.level];
263
350
  });
264
- if (p.absent === true) {
265
- return matches.length === 0
351
+ if (true === p.absent) {
352
+ return 0 === matches.length
266
353
  ? { pass: true, evidence: { absent: true } }
267
354
  : {
268
355
  pass: false,
269
356
  failureReason: `expected no ${p.level ?? 'console'} entries but found ${String(matches.length)}`,
270
- observed: `${String(matches.length)} ${p.level ?? 'console'} entr${matches.length === 1 ? 'y' : 'ies'}`,
357
+ observed: `${String(matches.length)} ${p.level ?? 'console'} entr${1 === matches.length ? 'y' : 'ies'}`,
271
358
  expected: `no ${p.level ?? 'console'} entries`,
272
359
  assertion: 'console.absent',
273
360
  evidence: matches.map((e) => e.data),
@@ -284,7 +371,7 @@ export function evalConsole(events, p) {
284
371
  };
285
372
  }
286
373
  export function evalAnimation(events, p) {
287
- const wantType = p.completed === true ? EventType.ANIM_END : EventType.ANIM_START;
374
+ const wantType = true === p.completed ? EventType.ANIM_END : EventType.ANIM_START;
288
375
  const hit = events.find((e) => {
289
376
  if (e.type !== wantType)
290
377
  return false;
@@ -330,7 +417,9 @@ export function evalSignal(events, p) {
330
417
  : `no signal matched ${JSON.stringify(p)}`,
331
418
  observed: sameName.length > 0
332
419
  ? `signal '${p.name ?? '(any)'}' fired ${String(sameName.length)}x, payload: ${JSON.stringify(sameName[0])}`
333
- : `signal '${p.name ?? '(any)'}' never fired in the window`,
420
+ : // Name what DID fire: a typo'd signal name and a genuinely dead action produce the same
421
+ // sentence otherwise, and the agent cannot tell them apart. See observed-in-window.ts.
422
+ `signal '${p.name ?? '(any)'}' never fired — ${describeObserved('signals', events.filter((e) => e.type === EventType.SIGNAL).map((e) => str(e.data['name']) ?? ''))}`,
334
423
  expected: p.dataMatches === undefined
335
424
  ? `signal '${p.name ?? '(any)'}' to fire`
336
425
  : `signal '${p.name ?? '(any)'}' with payload matching ${JSON.stringify(p.dataMatches)}`,
@@ -364,6 +453,12 @@ const SETTLE_ACTIVITY = new Set([
364
453
  EventType.DOM_REMOVED,
365
454
  EventType.DOM_ATTR,
366
455
  ]);
456
+ /** The three net-shaped event types a URL can be read off — the only ones dev tooling can produce. */
457
+ const NET_TYPES = new Set([
458
+ EventType.NET_PENDING,
459
+ EventType.NET_REQUEST,
460
+ EventType.NET_STREAM,
461
+ ]);
367
462
  /** Default quiet window — enough to absorb a render+xhr settle without waiting on slow polls. */
368
463
  const DEFAULT_QUIET_MS = 500;
369
464
  /**
@@ -372,8 +467,24 @@ const DEFAULT_QUIET_MS = 500;
372
467
  * now, not to any buffered event — so `now` is injected (CLAUDE.md rule 7), and the wait loop's
373
468
  * poll interval is what eventually flips this to pass once activity stops.
374
469
  */
375
- export function evalSettled(events, p, now) {
470
+ export function evalSettled(allEvents, p, now) {
376
471
  const quietMs = p.quietMs ?? DEFAULT_QUIET_MS;
472
+ // Dev-tooling traffic is the framework talking about ITSELF (see DevToolingChannel) and says
473
+ // nothing about whether the app finished its work — but it was holding settle open. Dropped from
474
+ // both halves of the calculation below (in-flight AND the quiet timer) and DISCLOSED on the
475
+ // evidence, never silently swallowed.
476
+ const ignoredDevTooling = [];
477
+ const events = allEvents.filter((e) => {
478
+ if (!NET_TYPES.has(e.type))
479
+ return true;
480
+ const url = str(e.data['url']);
481
+ if (!isDevToolingUrl(url))
482
+ return true;
483
+ if (url !== undefined && !ignoredDevTooling.includes(url))
484
+ ignoredDevTooling.push(url);
485
+ return false;
486
+ });
487
+ const disclosure = 0 === ignoredDevTooling.length ? {} : { ignoredDevTooling };
377
488
  // A request that STARTED (NET_PENDING) but never completed (NET_REQUEST with the same id) is
378
489
  // still in flight — the page is NOT settled no matter how quiet the DOM has gone. Without this,
379
490
  // a slow save reads as "settled" the instant its spinner stops mutating the DOM: the exact
@@ -417,16 +528,16 @@ export function evalSettled(events, p, now) {
417
528
  if (id === undefined)
418
529
  continue;
419
530
  const direction = str(e.data['direction']);
420
- if (direction === 'open')
531
+ if (StreamDirection.OPEN === direction)
421
532
  openStreams.add(id);
422
- else if (direction === 'close')
533
+ else if (StreamDirection.CLOSE === direction)
423
534
  openStreams.delete(id);
424
535
  }
425
536
  const streaming = openStreams.size;
426
537
  if (inFlight + streaming > 0) {
427
- const what = streaming === 0
538
+ const what = 0 === streaming
428
539
  ? `${String(inFlight)} request(s) still in flight`
429
- : inFlight === 0
540
+ : 0 === inFlight
430
541
  ? `${String(streaming)} response body(ies) still streaming`
431
542
  : `${String(inFlight)} request(s) in flight and ${String(streaming)} response body(ies) still streaming`;
432
543
  return {
@@ -435,7 +546,12 @@ export function evalSettled(events, p, now) {
435
546
  observed: what,
436
547
  expected: 'no requests in flight and no response bodies still streaming',
437
548
  assertion: 'settled.in-flight',
438
- evidence: { settled: false, inFlight, ...(streaming > 0 ? { streaming } : {}) },
549
+ evidence: {
550
+ settled: false,
551
+ inFlight,
552
+ ...(streaming > 0 ? { streaming } : {}),
553
+ ...disclosure,
554
+ },
439
555
  };
440
556
  }
441
557
  let lastT = -1;
@@ -449,12 +565,15 @@ export function evalSettled(events, p, now) {
449
565
  if (lastT < 0) {
450
566
  return {
451
567
  pass: true,
452
- evidence: { settled: true, quietForMs: null, note: 'no activity to settle' },
568
+ evidence: { settled: true, quietForMs: null, note: 'no activity to settle', ...disclosure },
453
569
  };
454
570
  }
455
571
  const quietForMs = now - lastT;
456
572
  if (quietForMs >= quietMs) {
457
- return { pass: true, evidence: { settled: true, quietForMs, lastActivity: lastType } };
573
+ return {
574
+ pass: true,
575
+ evidence: { settled: true, quietForMs, lastActivity: lastType, ...disclosure },
576
+ };
458
577
  }
459
578
  return {
460
579
  pass: false,
@@ -462,6 +581,11 @@ export function evalSettled(events, p, now) {
462
581
  observed: `last activity was ${String(lastType)}, ${String(quietForMs)}ms ago`,
463
582
  expected: `${String(quietMs)}ms of quiet`,
464
583
  assertion: 'settled.quiet',
465
- evidence: { quietForMs, lastActivity: lastType },
584
+ evidence: { quietForMs, lastActivity: lastType, ...disclosure },
585
+ // The one failure in this file that knows exactly when it could stop being one: if nothing else
586
+ // happens, the window closes in this many ms. A waiter that re-checks THEN instead of on its next
587
+ // blind tick stops paying up to a full poll interval on the hottest call in the product. Only a
588
+ // hint — the predicate is still evaluated at that moment, and can still fail.
589
+ retryAfterMs: quietMs - quietForMs,
466
590
  };
467
591
  }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * One readable sentence when a predicate does not parse — never the zod array.
3
+ *
4
+ * Measured 2026-08-10: **9 of 58 tool errors that day were a serialized zod issue array**, all on
5
+ * `reticle_act_and_wait`, `reticle_wait_for` and `reticle_assert` — the three tools that produced
6
+ * every action-derived finding in the dataset. The least readable error we emit was landing on the
7
+ * highest-value path, and an agent had to `JSON.parse` an error string to learn which field it got
8
+ * wrong.
9
+ *
10
+ * `PredicateSchema.parse()` throws a `ZodError` whose `.message` IS that array, and three handlers
11
+ * call it directly. This wraps it once, so every caller gets the same shape rather than each
12
+ * growing its own catch — the pattern #108 asks for: name the parameter, say whether anything ran,
13
+ * show a valid call.
14
+ */
15
+ import { z } from 'zod';
16
+ import { PredicateSchema } from './predicate-eval.js';
17
+ /**
18
+ * Parse a predicate, or throw an Error whose message is a sentence.
19
+ *
20
+ * Deliberately still THROWS: every call site already handles a throw, and turning this into a
21
+ * result type would mean touching three handlers to gain nothing the message does not already say.
22
+ */
23
+ export declare function parsePredicate(input: unknown): z.infer<typeof PredicateSchema>;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * One readable sentence when a predicate does not parse — never the zod array.
3
+ *
4
+ * Measured 2026-08-10: **9 of 58 tool errors that day were a serialized zod issue array**, all on
5
+ * `reticle_act_and_wait`, `reticle_wait_for` and `reticle_assert` — the three tools that produced
6
+ * every action-derived finding in the dataset. The least readable error we emit was landing on the
7
+ * highest-value path, and an agent had to `JSON.parse` an error string to learn which field it got
8
+ * wrong.
9
+ *
10
+ * `PredicateSchema.parse()` throws a `ZodError` whose `.message` IS that array, and three handlers
11
+ * call it directly. This wraps it once, so every caller gets the same shape rather than each
12
+ * growing its own catch — the pattern #108 asks for: name the parameter, say whether anything ran,
13
+ * show a valid call.
14
+ */
15
+ import { z } from 'zod';
16
+ import { PredicateSchema } from './predicate-eval.js';
17
+ /** A valid call, short enough to sit inside an error without becoming the error. */
18
+ const EXAMPLE = '{ kind: "signal", name: "todos:loaded" }';
19
+ /** `path: ["net","urlContains"]` → `net.urlContains`; an empty path means the object itself. */
20
+ function pathOf(issue) {
21
+ return 0 === issue.path.length ? 'the predicate' : issue.path.map(String).join('.');
22
+ }
23
+ /**
24
+ * One issue, as a clause a human or an agent can act on.
25
+ *
26
+ * `unrecognized_keys` is the common case by far and the one worth spelling out — it is what a
27
+ * plausible-but-wrong field name produces, and the key itself is the whole answer.
28
+ */
29
+ function describeIssue(issue) {
30
+ if (z.ZodIssueCode.unrecognized_keys === issue.code) {
31
+ return `unknown field ${issue.keys.join(', ')}`;
32
+ }
33
+ return `${pathOf(issue)}: ${issue.message}`;
34
+ }
35
+ /**
36
+ * Parse a predicate, or throw an Error whose message is a sentence.
37
+ *
38
+ * Deliberately still THROWS: every call site already handles a throw, and turning this into a
39
+ * result type would mean touching three handlers to gain nothing the message does not already say.
40
+ */
41
+ export function parsePredicate(input) {
42
+ const parsed = PredicateSchema.safeParse(input);
43
+ if (parsed.success)
44
+ return parsed.data;
45
+ const kind = 'object' === typeof input && null !== input && 'kind' in input
46
+ ? String(input['kind'])
47
+ : 'unknown';
48
+ // Bounded on purpose: a union rejection can produce one issue per member, and pasting all of them
49
+ // back is how the zod array became unreadable in the first place.
50
+ const issues = parsed.error.issues.slice(0, 3).map(describeIssue).join('; ');
51
+ throw new Error(`that predicate did not parse (kind "${kind}"): ${issues}. Nothing ran — the predicate was ` +
52
+ `not evaluated, so no verdict was produced. Call reticle_tools for the fields this kind ` +
53
+ `accepts. A valid call looks like: ${EXAMPLE}`);
54
+ }
@@ -1,8 +1,8 @@
1
- import { type CommandResult, type ReticleEvent } from '@reticlehq/core';
1
+ import { PredicateKind, type CommandResult, type ReticleEvent } from '@reticlehq/core';
2
2
  import type { ExpectedLink } from '../capsule/divergence.js';
3
3
  import { type AmbientCounts } from '../journal/ambient.js';
4
4
  import { PredicateSchema, type Predicate, type EvalResult } from './predicate-eval.js';
5
- export { PredicateSchema };
5
+ export { PredicateSchema, PredicateKind };
6
6
  export type { Predicate, EvalResult };
7
7
  /** The subset of Session the predicate engine needs — keeps it testable with a fake. */
8
8
  export interface PredicateSession {
@@ -17,6 +17,11 @@ export interface PredicateSession {
17
17
  * Optional: a session without ambient learning simply omits it and settle behaves as before.
18
18
  */
19
19
  ambientCounts?(): AmbientCounts;
20
+ /**
21
+ * Subscribe to session disconnect. Returns an unsubscribe function. Optional: a session without
22
+ * this hook (e.g. tests that never disconnect) simply leaves in-flight predicates until timeout.
23
+ */
24
+ onDisconnect?(listener: () => void): () => void;
20
25
  }
21
26
  export declare function evaluatePredicate(session: PredicateSession, predicate: Predicate, since?: number, diagnose?: boolean): Promise<EvalResult>;
22
27
  /**