@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
@@ -8,6 +8,51 @@
8
8
  * on stable, human-authored substrings of the thrown messages (see session-manager.ts, the flow/
9
9
  * baseline stores). No clock, no IO — unit-testable in isolation.
10
10
  */
11
+ import { TRANSPORT_LIMITS } from '@reticlehq/core';
12
+ import { SELF_RECOVERING_MARKER } from '../session/no-session-diagnosis.js';
13
+ /** Marks where an over-long message had its middle removed. Named so tests assert it by value. */
14
+ const ELISION = '… [elided] …';
15
+ /**
16
+ * Bound a message before the agent ever sees it, keeping BOTH ends.
17
+ *
18
+ * Error messages interpolate caller-supplied values — a ref, a baseline name, a selector — and
19
+ * nothing upstream bounds them, so a 100KB argument came back as a 100KB tool result. Capping here
20
+ * rather than at each throw site is deliberate: this is the single funnel every tool error passes
21
+ * through, so it also covers the tools nobody has written yet.
22
+ *
23
+ * The middle is what goes. Head-truncation would be simpler and wrong: the recovery table keys off
24
+ * substrings that sit at the END of the message ("… no longer resolves to an element"), so cutting
25
+ * the tail severs the part that makes an error recognizable and re-labels a known condition as a
26
+ * possible Reticle defect.
27
+ */
28
+ function capMessage(message) {
29
+ const max = TRANSPORT_LIMITS.MAX_ERROR_LENGTH;
30
+ if (max >= message.length)
31
+ return message;
32
+ const keep = Math.floor((max - ELISION.length) / 2);
33
+ return `${message.slice(0, keep)}${ELISION}${message.slice(message.length - keep)}`;
34
+ }
35
+ /**
36
+ * Messages that already spell out the exact retry, so any hint we add can only argue with them.
37
+ *
38
+ * The no-session diagnosis is one (it inspected the machine and named the cause). The native-input
39
+ * refusal is the other: it names the tool, the argument and the follow-up call to make instead — and
40
+ * it was still collecting the defect ask, inviting a bug report about a refusal that had just told
41
+ * the agent precisely what to do.
42
+ */
43
+ const SELF_RECOVERING = [
44
+ /cannot drive native input/i,
45
+ // The unknown-session refusal now lists the live ids inline. Appending "call reticle_sessions for
46
+ // the current ids" to a message that just named them contradicts the shorter path it offers, and
47
+ // a contradicted instruction is how one agent came to retry a dead id twelve times.
48
+ /Connected right now:/i,
49
+ ];
50
+ /** A message that already carries its own concrete next action, so nothing should be appended. */
51
+ function isSelfRecovering(message) {
52
+ if (message.includes(SELF_RECOVERING_MARKER))
53
+ return true;
54
+ return SELF_RECOVERING.some((pattern) => pattern.test(message));
55
+ }
11
56
  /** The recovery hints, named so they are not free strings and can be asserted in tests. */
12
57
  export const RECOVERY = {
13
58
  NO_SESSION: 'No app is connected to Reticle. Ask the human to start their app in dev with @reticlehq/browser ' +
@@ -21,18 +66,71 @@ export const RECOVERY = {
21
66
  'the tab to the front, or run `reticle drive <url>` for a guaranteed scriptable context.',
22
67
  MISSING_BASELINE: 'That baseline does not exist yet. Call reticle_baseline { action: "list" } to see saved names, or ' +
23
68
  'reticle_baseline { action: "save", name } to capture one before diffing against it.',
24
- MISSING_RECORDING: 'No recording by that name is in progress. Start one with reticle_record { action: "start", name } ' +
25
- 'before annotating, stopping, or saving it.',
69
+ MISSING_RECORDING:
70
+ // `recordingName`, not `name`. The hint said `name`, so an agent that followed it exactly got
71
+ // InvalidParams — a recovery that fails is worse than none, because it spends the retry.
72
+ 'No recording by that name is in progress. Start one with ' +
73
+ 'reticle_record { action: "start", recordingName } before annotating, stopping, or saving it.',
26
74
  STALE_REF: 'That ref is stale: refs are invalidated whenever the DOM re-renders, so any action that ' +
27
75
  'navigated, opened a modal, re-sorted a list or changed the page invalidates every ref taken ' +
28
76
  'before it. Reticle refuses here rather than clicking whatever now occupies that slot. Call ' +
29
77
  'reticle_query again for a fresh ref and retry the action — and prefer reticle_act_and_wait ' +
30
78
  '{ until } when an action changes the page, so the next ref is taken after it settles.',
79
+ INVALID_NAME: 'That name is not a usable one. A flow, baseline or recording name must be a single safe path ' +
80
+ 'segment — letters, digits, dash and underscore only, starting with a letter or digit, at most ' +
81
+ '64 characters. No slashes, no "..", no leading dot: the name becomes a filename under ' +
82
+ '.reticle/, so anything that could escape that directory is refused. Retry with a slug like ' +
83
+ '"checkout-happy-path". This is an invalid call, not a Reticle defect: there is nothing to report.',
84
+ NO_SUCH_OPTION: 'That <select> has no option with the value you asked for, and the message above lists the ones ' +
85
+ 'it does have. Reticle refuses rather than assigning it: an unmatched value deselects everything, ' +
86
+ 'and the change event that follows has been observed persisting an empty setting into the app. ' +
87
+ 'Retry with one of the listed values — note they are option VALUES, which often differ from the ' +
88
+ 'visible labels shown in brackets. This is an invalid call, not a Reticle defect.',
31
89
  BAD_ARGUMENTS: "That call did not match the tool's schema — the message above names the tool and the exact " +
32
90
  "argument it wanted. Re-read that tool's parameters and retry with the missing or corrected " +
33
91
  'argument. This is an invalid call, not a Reticle defect: there is nothing to report.',
92
+ WRONG_TARGET: 'That action does not apply to this element — the ref resolved, the element just cannot take it ' +
93
+ '(you cannot fill a <button>, submit outside a form, or upload to anything but <input ' +
94
+ 'type="file">). Call reticle_snapshot or reticle_query to find the control that does, or ' +
95
+ 'reticle_inspect on this ref to see what it actually is. This is an invalid call, not a Reticle ' +
96
+ 'defect: there is nothing to report.',
97
+ NOT_EDITABLE: 'The field is disabled or readonly, so a user could not edit it either — forcing the value would ' +
98
+ 'put the app in a state nobody can reach, which is why Reticle refuses. This is usually the app ' +
99
+ 'telling you something: drive whatever ENABLES the field (open the editor, satisfy the ' +
100
+ 'precondition) and act again. If the field should have been editable, that is a bug in the app, ' +
101
+ 'not in Reticle.',
102
+ CONFIRM_DANGEROUS: 'Reticle blocked a potentially destructive control (delete, remove, revoke…) on purpose. If you ' +
103
+ 'mean it, retry the same action with args.confirmDangerous set to true. This is a deliberate ' +
104
+ 'refusal, not a defect: there is nothing to report.',
105
+ UNSUPPORTED_SURFACE: 'That surface is not supported yet, and Reticle refuses rather than pretending — a rich-text ' +
106
+ 'editor keeps its own document model, so writing to the DOM would look right and submit the old ' +
107
+ 'content. Drive the same outcome another way (a plain input, a command, a keyboard action) or ' +
108
+ 'assert on the result instead of typing it. Known gap, already reported: no need to file it.',
34
109
  TOKEN_REQUIRED: 'The bridge binds beyond localhost and requires a pairing token. Set the same token in the SDK ' +
35
110
  'init (@reticlehq/core) and the Reticle server config, then reconnect.',
111
+ SCOPE_MISMATCH: 'A tab IS connected — it just belongs to a different project than the daemon is scoped to, so ' +
112
+ 'nothing is wrong with the app and there is nothing to report. The message above lists each ' +
113
+ 'connected session with its sessionId: pass that sessionId to this tool to target it, or ' +
114
+ "restart the daemon from that app's directory so it becomes the default. Call reticle_sessions " +
115
+ 'for the same list at any time.',
116
+ SESSION_GONE: 'The tab this call was targeting disconnected while the command was in flight, so the command ' +
117
+ 'never completed — do not assume it applied or that it did not. Call reticle_sessions: a tab ' +
118
+ 'that reloaded comes back under a NEW sessionId (every ref taken before it is gone — re-query), ' +
119
+ 'and an empty list means the page was closed and the human has to reopen it.',
120
+ COMMAND_TIMEOUT: 'The page did not answer within the command window. That is a fact about the page, not a Reticle ' +
121
+ 'failure: check reticle_sessions for `throttled`/`stale` on this session — a backgrounded tab is ' +
122
+ 'throttled and may never answer, so ask the human to bring it to the front or drive your own ' +
123
+ 'browser with reticle_lease. If the tab is in front, the page is busy or blocked (a long ' +
124
+ 'synchronous task, an alert/confirm dialog); reticle_console usually shows what it hit. Retry ' +
125
+ 'once the cause is addressed.',
126
+ FLOW_STEP_MISSING: 'A recorded step points at an element that is not on the page now. Either the app changed under ' +
127
+ 'the flow — run reticle_flow_heal to re-anchor it, then reticle_flow_verify — or an earlier step ' +
128
+ 'did not do what it used to, leaving the run on the wrong page: check reticle_snapshot to see ' +
129
+ 'where it actually is. This is app/flow drift, which is exactly what the flow exists to catch.',
130
+ NO_POOL: 'Reticle cannot launch its own browser here: the daemon-managed pool is absent, or Playwright ' +
131
+ 'has no Chromium installed (the message above says which). Run Reticle via `reticle mcp`, and ' +
132
+ 'ask the human for `npx playwright install chromium` if that is what is missing. Meanwhile drive ' +
133
+ 'a tab the human already has open — reticle_sessions lists them.',
36
134
  };
37
135
  /** Ordered match rules; the first hit wins. Substrings track the thrown messages they recover. */
38
136
  const RULES = [
@@ -43,9 +141,51 @@ const RULES = [
43
141
  { match: /no baseline named/i, hint: RECOVERY.MISSING_BASELINE },
44
142
  { match: /no (?:active|compiled) recording named/i, hint: RECOVERY.MISSING_RECORDING },
45
143
  { match: /pairing token is required/i, hint: RECOVERY.TOKEN_REQUIRED },
144
+ // Three conditions the daemon understands perfectly and still asked for a bug report about. Each
145
+ // needs its own rule: none of them contains "no browser session connected" (the scope miss is
146
+ // "no browser session FOR project 'x'", which is the opposite claim — sessions exist).
147
+ {
148
+ match: /scope mismatch|session\(s\) ARE connected under a different project/i,
149
+ hint: RECOVERY.SCOPE_MISMATCH,
150
+ },
151
+ { match: /^session disconnected$|session .* never connected/i, hint: RECOVERY.SESSION_GONE },
152
+ { match: /^command '[^']*' timed out after/i, hint: RECOVERY.COMMAND_TIMEOUT },
153
+ // Flow replay: the recorded anchor is gone. Ordinary drift, and the tool that fixes it exists.
154
+ { match: /did not resolve in current page/i, hint: RECOVERY.FLOW_STEP_MISSING },
155
+ {
156
+ match: /browser pool (?:unavailable|is shut down)|Chromium is not installed/i,
157
+ hint: RECOVERY.NO_POOL,
158
+ },
46
159
  // The commonest post-action condition there is. Unmatched, it fell through to FEEDBACK_ASK and
47
160
  // told the agent a successful click's aftermath might be a bug in Reticle.
48
161
  { match: /no longer resolves to an element/i, hint: RECOVERY.STALE_REF },
162
+ { match: /no <option> with value/i, hint: RECOVERY.NO_SUCH_OPTION },
163
+ // Every guard in the browser's executeAction. These are Reticle-authored refusals of an invalid
164
+ // call, and ALL of them used to fall through to the feedback ask — including the destructive-action
165
+ // block, which carries its own retry instruction and was still telling the agent Reticle might be
166
+ // broken. Order matters: contenteditable and disabled/readonly are `cannot <verb> …` messages too,
167
+ // so they must be tested before the general wrong-target rule.
168
+ { match: /contenteditable/i, hint: RECOVERY.UNSUPPORTED_SURFACE },
169
+ { match: /cannot \w+ a (disabled|readonly) </i, hint: RECOVERY.NOT_EDITABLE },
170
+ // Three spellings ship — "action", "native action", "WebMCP tool" — and the rule matched one, so
171
+ // two thirds of the same deliberate refusal still read as a possible defect.
172
+ {
173
+ match: /potentially destructive (?:\w+ )*(?:action|tool) blocked/i,
174
+ hint: RECOVERY.CONFIRM_DANGEROUS,
175
+ },
176
+ // Split in two because `^` binds to the FIRST alternative only. As one pattern this read as though
177
+ // the anchor governed all four spellings; it never did, and the next person to add a fifth would
178
+ // have had a coin-flip's chance of getting the behaviour they intended. The anchored spelling is
179
+ // one Reticle authors at the start of its own message; the other three appear mid-message.
180
+ { match: /^cannot [\w()]+ (?:a|an|into a|on a) </i, hint: RECOVERY.WRONG_TARGET },
181
+ {
182
+ match: /no form to submit|upload target must be|is not an HTMLElement/i,
183
+ hint: RECOVERY.WRONG_TARGET,
184
+ },
185
+ // Authored by Reticle, about the caller's arguments: the message already names the valid answers.
186
+ { match: /^unknown action '/i, hint: RECOVERY.BAD_ARGUMENTS },
187
+ { match: /^unsupported query strategy '/i, hint: RECOVERY.BAD_ARGUMENTS },
188
+ { match: /^\w+ requires a string `\w+`/i, hint: RECOVERY.BAD_ARGUMENTS },
49
189
  // A message naming a `reticle_*` tool is one WE authored about our own API — an invalid call, not
50
190
  // an unanticipated failure. Left unmatched it collected the feedback ask, which pushed agents to
51
191
  // file reports about their own bad arguments. Kept LAST so a specific rule above always wins.
@@ -54,14 +194,108 @@ const RULES = [
54
194
  hint: RECOVERY.BAD_ARGUMENTS,
55
195
  },
56
196
  ];
57
- /** The actionable next move for a known error message, or undefined when none is recognized. */
197
+ /**
198
+ * The actionable next move for a known error message, or undefined when none is recognized.
199
+ *
200
+ * A message that already carries its own next action gets NO second one. The no-session diagnosis
201
+ * inspects the machine and says which of three causes this actually is; pairing that with the
202
+ * generic "ask the human to start their app" produced a result that contradicted itself — the error
203
+ * saying a server is running and the recovery saying to go start one.
204
+ */
58
205
  export function recoveryFor(message) {
206
+ if (isSelfRecovering(message))
207
+ return undefined;
59
208
  for (const rule of RULES) {
60
209
  if (rule.match.test(message))
61
210
  return rule.hint;
62
211
  }
63
212
  return undefined;
64
213
  }
214
+ /**
215
+ * A schema rejection: the call named a parameter that does not exist, or gave one the wrong type.
216
+ *
217
+ * Matched by the SHAPES the validators actually produce (zod's codes, and the MCP layer's wrapper),
218
+ * because these are our own machinery talking, not the app.
219
+ *
220
+ * `did not parse` is the shape `parsePredicate` produces. It was added because removing the raw zod
221
+ * array from that path ALSO removed the codes this pattern keys on — so a malformed predicate
222
+ * stopped being recognised as the agent's mistake and started being blamed on Reticle, complete with
223
+ * a request for a bug report. The e2e battery caught it (`no bad argument is blamed on Reticle`);
224
+ * no unit test could, which is why one now exists below.
225
+ */
226
+ /** Max issues rendered — a union rejection can emit one per member, which is how the array became unreadable. */
227
+ const MAX_RENDERED_ISSUES = 3;
228
+ function isZodIssue(value) {
229
+ if (typeof value !== 'object' || null === value)
230
+ return false;
231
+ const v = value;
232
+ return 'string' === typeof v['code'] && 'string' === typeof v['message'];
233
+ }
234
+ /** `["net","urlContains"]` → `net.urlContains`; an empty path means the object itself. */
235
+ function issueClause(issue) {
236
+ if ('unrecognized_keys' === issue.code && Array.isArray(issue.keys)) {
237
+ return `unknown field ${issue.keys.map(String).join(', ')}`;
238
+ }
239
+ const path = issue.path ?? [];
240
+ return `${0 === path.length ? 'the arguments' : path.map(String).join('.')}: ${issue.message}`;
241
+ }
242
+ /**
243
+ * A serialized zod issue array, rendered as a sentence. Anything else is returned untouched.
244
+ *
245
+ * This is the one boundary every agent-visible tool failure crosses, which is why the rewrite lives
246
+ * here rather than in each tool. `parsePredicate` solved this for the three predicate tools by never
247
+ * producing the array; it cannot help anywhere else, because the MCP SDK validates a tool's schema
248
+ * BEFORE our handler runs. So `reticle_session` with no action — the commonest mistake there is —
249
+ * never reaches the friendly "unknown action … expected: [tune, yield, …]" its handler already
250
+ * builds, and the agent gets the raw array instead. Driven live against the shipped daemon.
251
+ *
252
+ * Conservative by construction: it only rewrites a JSON ARRAY whose every element carries both
253
+ * `code` and `message`. A tool that legitimately returns JSON is left alone.
254
+ */
255
+ export function zodArrayAsSentence(message) {
256
+ const text = message.trim();
257
+ if (!text.startsWith('['))
258
+ return message;
259
+ let parsed;
260
+ try {
261
+ parsed = JSON.parse(text);
262
+ }
263
+ catch {
264
+ return message; // not JSON at all — e.g. prose that happens to open with a bracket
265
+ }
266
+ if (!Array.isArray(parsed) || 0 === parsed.length || !parsed.every(isZodIssue))
267
+ return message;
268
+ const shown = parsed.slice(0, MAX_RENDERED_ISSUES).map(issueClause).join('; ');
269
+ const rest = parsed.length - MAX_RENDERED_ISSUES;
270
+ return rest > 0 ? `${shown} (and ${String(rest)} more)` : shown;
271
+ }
272
+ /**
273
+ * `unknown field` and `<path>: Required` are the shapes `zodArrayAsSentence` produces.
274
+ *
275
+ * Added for the same reason `did not parse` was: rendering the array as a sentence REMOVES the very
276
+ * codes (`invalid_type`, `unrecognized_keys`) this pattern matched on, so a schema rejection fell
277
+ * through to the generic branch and came back as "may be a defect in Reticle" — worse than the dump
278
+ * it replaced, because it also asks for a bug report about the agent's own malformed call. This repo
279
+ * has now made that mistake twice; the second time a test caught it before CI did.
280
+ *
281
+ * Widened a THIRD time for PR #182, which formats the MCP SDK's validation error at the argument
282
+ * boundary — "Missing required parameter for reticle_session: action (one of: …)". That is better
283
+ * prose than the field-level sentence below (it names the TOOL and the allowed VALUES) and it was
284
+ * authored before this formatter existed, so it must not be punished for arriving first.
285
+ */
286
+ const ARGUMENT_REJECTION = /Unrecognized key|unrecognized_keys|invalid_type|did not parse|unknown field|: Required\b|Missing required parameter|Invalid parameter for|Invalid arguments for tool|Unknown parameters? for|^Required |Expected \w+, received/i;
287
+ /**
288
+ * What to say instead of asking for a bug report. The schema already stated the problem precisely;
289
+ * this only says who can fix it and where to look.
290
+ */
291
+ const ARGUMENT_RECOVERY = "That call did not match the tool's schema — the message above names the parameter. Nothing ran, " +
292
+ 'so no result is affected. Call reticle_tools { names: ["<tool>"] } for its exact parameters and ' +
293
+ 'retry.';
294
+ /**
295
+ * `invalid <thing> name: <value>` — the shape every name validator in the server throws. Matched on
296
+ * the phrasing rather than per-caller so a new store that validates a name is covered by default.
297
+ */
298
+ const INVALID_NAME_REJECTION = /^invalid (?:[a-z]+ )*name: /i;
65
299
  /**
66
300
  * The ask attached to errors we do NOT recognize. An unrecognized error is, by definition, the case
67
301
  * where Reticle failed in a way nobody anticipated — the single highest-value moment to hear from the
@@ -79,7 +313,24 @@ export const FEEDBACK_ASK = 'This error is not one Reticle recognizes, which mea
79
313
  * known; the feedback ask is added when it is NOT — the two are mutually exclusive by design, so the
80
314
  * agent always gets exactly one next move.
81
315
  */
82
- export function buildErrorPayload(message) {
316
+ export function buildErrorPayload(rawMessage) {
317
+ // Render the zod array BEFORE capping: the cap would otherwise truncate the JSON mid-issue and
318
+ // leave an unparseable fragment as the agent's error text — the worst of both shapes.
319
+ const message = capMessage(zodArrayAsSentence(rawMessage));
320
+ // A self-diagnosing message gets NEITHER a generic recovery nor the feedback ask. It already
321
+ // inspected the machine and named the cause; a second, contradictory hint is noise, and inviting a
322
+ // bug report about a condition Reticle diagnosed itself is exactly backwards.
323
+ if (isSelfRecovering(message))
324
+ return { error: message };
325
+ // An argument rejection is the agent's to fix, not a Reticle defect. Reported from the field: a
326
+ // clean `unrecognized_keys: ["value"]` came back wrapped in "may be a defect in Reticle", which
327
+ // spends the agent's turn and fills the feedback channel with reports about malformed calls.
328
+ if (ARGUMENT_REJECTION.test(message))
329
+ return { error: message, recovery: ARGUMENT_RECOVERY };
330
+ // Reticle's own name validators rejecting a caller's value: an argument rejection that the schema
331
+ // cannot express (the shape is a runtime pattern, not a type), so it needs its own arm.
332
+ if (INVALID_NAME_REJECTION.test(message))
333
+ return { error: message, recovery: RECOVERY.INVALID_NAME };
83
334
  const recovery = recoveryFor(message);
84
335
  return recovery !== undefined
85
336
  ? { error: message, recovery }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The one line that tells an agent it can push back — worded for what just happened.
3
+ *
4
+ * Feedback is the highest-yield channel this product has: **14 reports over two days produced
5
+ * roughly 11 of the fixes in 2.6.0**, against ~6,100 numeric events that produced a funnel which
6
+ * turned out to be broken. Prose from an agent at the moment of friction beats any structured field
7
+ * we could add.
8
+ *
9
+ * It reached those 14 through a standing instruction in the MCP handshake — paid once at connect.
10
+ * The gap that leaves is the agent who hits something mid-task and is concentrating on the problem
11
+ * rather than on remembering that a feedback tool exists.
12
+ *
13
+ * So: invite at the moment of friction, and vary the wording. A fixed string repeated forty times
14
+ * is invisible by call five; a line that names what just went wrong is read because it is about the
15
+ * thing the agent is already looking at. ~12 tokens against a ~4,546 tok/turn surface.
16
+ *
17
+ * Every invite is counted (`feedbackPrompted`). If `feedback_submitted ÷ feedbackPrompted` stays
18
+ * flat, this is decoration and should be deleted — instrumenting our own nudge is the difference
19
+ * between a designed system and a hope.
20
+ */
21
+ /** What just happened, as far as the chokepoint can tell. Ordered most specific first. */
22
+ export declare const FrictionKind: {
23
+ /** A tool that does not exist — the agent named a capability it expected us to have. */
24
+ readonly UNKNOWN_TOOL: "unknown_tool";
25
+ /** A verdict came back `unknown`: we drove the app and could not tell what happened. */
26
+ readonly UNKNOWN_VERDICT: "unknown_verdict";
27
+ /** The same tool, several times in a row. */
28
+ readonly REPEATING: "repeating";
29
+ /** The tool refused or errored. */
30
+ readonly REFUSED: "refused";
31
+ };
32
+ export type FrictionKind = (typeof FrictionKind)[keyof typeof FrictionKind];
33
+ /**
34
+ * Which friction, if any, this result represents.
35
+ *
36
+ * Deliberately reads only what the chokepoint already has: no handler changes, and nothing here can
37
+ * see app data.
38
+ */
39
+ export declare function frictionOf(facts: {
40
+ unknownTool: boolean;
41
+ verifiedUnknown: boolean;
42
+ repeatRun: number;
43
+ errored: boolean;
44
+ /** Did this call produce a verdict? A verdict is progress, and progress is not being stuck. */
45
+ producedVerdict: boolean;
46
+ }): FrictionKind | undefined;
47
+ /** The line for a friction kind. */
48
+ export declare function inviteFor(kind: FrictionKind): string;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The one line that tells an agent it can push back — worded for what just happened.
3
+ *
4
+ * Feedback is the highest-yield channel this product has: **14 reports over two days produced
5
+ * roughly 11 of the fixes in 2.6.0**, against ~6,100 numeric events that produced a funnel which
6
+ * turned out to be broken. Prose from an agent at the moment of friction beats any structured field
7
+ * we could add.
8
+ *
9
+ * It reached those 14 through a standing instruction in the MCP handshake — paid once at connect.
10
+ * The gap that leaves is the agent who hits something mid-task and is concentrating on the problem
11
+ * rather than on remembering that a feedback tool exists.
12
+ *
13
+ * So: invite at the moment of friction, and vary the wording. A fixed string repeated forty times
14
+ * is invisible by call five; a line that names what just went wrong is read because it is about the
15
+ * thing the agent is already looking at. ~12 tokens against a ~4,546 tok/turn surface.
16
+ *
17
+ * Every invite is counted (`feedbackPrompted`). If `feedback_submitted ÷ feedbackPrompted` stays
18
+ * flat, this is decoration and should be deleted — instrumenting our own nudge is the difference
19
+ * between a designed system and a hope.
20
+ */
21
+ /** What just happened, as far as the chokepoint can tell. Ordered most specific first. */
22
+ export const FrictionKind = {
23
+ /** A tool that does not exist — the agent named a capability it expected us to have. */
24
+ UNKNOWN_TOOL: 'unknown_tool',
25
+ /** A verdict came back `unknown`: we drove the app and could not tell what happened. */
26
+ UNKNOWN_VERDICT: 'unknown_verdict',
27
+ /** The same tool, several times in a row. */
28
+ REPEATING: 'repeating',
29
+ /** The tool refused or errored. */
30
+ REFUSED: 'refused',
31
+ };
32
+ /**
33
+ * Runs of one tool before it counts as being stuck.
34
+ *
35
+ * Three, matching the verdict nudge: `reticle_act` led the field data at 110 consecutive-repeat
36
+ * runs, and a run of three is where a deliberate retry becomes a loop.
37
+ */
38
+ const REPEAT_INVITE_AT = 3;
39
+ const LINES = {
40
+ [FrictionKind.UNKNOWN_TOOL]: 'no such tool — reticle_feedback tells us what you expected it to do, which is how it gets built',
41
+ [FrictionKind.UNKNOWN_VERDICT]: 'Reticle could not tell what happened here. If you expected otherwise, reticle_feedback — an ' +
42
+ 'unknown verdict is our defect, not yours',
43
+ [FrictionKind.REPEATING]: 'stuck on the same call? reticle_feedback — say what you were trying to do and it gets fixed',
44
+ [FrictionKind.REFUSED]: 'if that diagnosis was wrong or unhelpful, reticle_feedback — error messages are a product ' +
45
+ 'surface and we fix the ones that fail',
46
+ };
47
+ /**
48
+ * Which friction, if any, this result represents.
49
+ *
50
+ * Deliberately reads only what the chokepoint already has: no handler changes, and nothing here can
51
+ * see app data.
52
+ */
53
+ export function frictionOf(facts) {
54
+ if (facts.unknownTool)
55
+ return FrictionKind.UNKNOWN_TOOL;
56
+ if (facts.verifiedUnknown)
57
+ return FrictionKind.UNKNOWN_VERDICT;
58
+ // Repetition is only friction when it is repetition WITHOUT PROGRESS. Found by driving the Tauri
59
+ // smoke app: three consecutive SUCCESSFUL act_and_wait calls, each producing a verdict, were
60
+ // answered with "stuck on the same call?" — nagging the exact loop this release exists to
61
+ // encourage. A verdict is progress.
62
+ if (facts.repeatRun >= REPEAT_INVITE_AT && !facts.producedVerdict)
63
+ return FrictionKind.REPEATING;
64
+ if (facts.errored)
65
+ return FrictionKind.REFUSED;
66
+ return undefined;
67
+ }
68
+ /** The line for a friction kind. */
69
+ export function inviteFor(kind) {
70
+ return LINES[kind];
71
+ }
@@ -12,7 +12,18 @@ export declare const FEEDBACK_PROMPT: {
12
12
  readonly ask: "Ask the human, in your own words and only once: how did this verification go, and what would have made Reticle more useful? If they answer, file it with `reticle feedback` (they run it) or relay it yourself.";
13
13
  readonly command: "reticle feedback";
14
14
  };
15
- /** Tools whose completion means "a verification just happened" — the only moment worth asking at. */
15
+ /**
16
+ * Tools whose completion means "a verification just happened" — the only moment worth asking at,
17
+ * and the set that decides what `verification_completed` counts.
18
+ *
19
+ * ACT_AND_WAIT belongs here and was missing for a long time. It declares `verified` in its
20
+ * outputSchema, returns the whole verdict block, and its own description calls it "one hop for the
21
+ * act->observe->assert loop" — it IS the verification path most agents take. The guard that should
22
+ * have caught the omission keyed on the tool NAME (/assert|verify/), which act_and_wait does not
23
+ * match, so it slipped through in silence. Measured over a day of real telemetry: act_and_wait 14
24
+ * calls, assert ZERO, verification_completed 2. Agents were verifying the entire time and the metric
25
+ * could not see it. The contract test now checks the SHAPE instead.
26
+ */
16
27
  export declare const VERIFICATION_TOOLS: ReadonlySet<string>;
17
28
  /**
18
29
  * The human prompt, when one is due. Resettable for tests.
@@ -30,6 +30,9 @@ function sessionFacts(deps, sessionId) {
30
30
  return {
31
31
  ...(session.runtime !== undefined ? { runtime: session.runtime } : {}),
32
32
  ...(session.engine !== undefined ? { engine: session.engine } : {}),
33
+ // The app's own account of what it is. Without this the reported stack came from the daemon's
34
+ // working directory, so feedback from one app was filed against another's framework.
35
+ ...(0 < session.adapters.length ? { adapters: session.adapters } : {}),
33
36
  driven,
34
37
  };
35
38
  }
@@ -83,7 +86,10 @@ export const FEEDBACK_TOOLS = [
83
86
  .describe('Only to attach a specific tab s runtime/engine context. Omit it — a report about a session that never connected is still worth filing.'),
84
87
  },
85
88
  outputSchema: {
89
+ /** Confirmed delivery. Always false here: the agent path queues and returns. */
86
90
  sent: z.boolean(),
91
+ /** Validated, redacted and queued. This is the one to read. */
92
+ accepted: z.boolean(),
87
93
  reason: z.string().optional(),
88
94
  redacted: z.array(z.string()),
89
95
  context: z.unknown(),
@@ -106,12 +112,17 @@ export const FEEDBACK_TOOLS = [
106
112
  ...optionalText(args, 'impact'),
107
113
  ...optionalText(args, 'currentApproach'),
108
114
  ...optionalText(args, 'model'),
109
- }, { session: sessionFacts(deps, asString(args['sessionId'])) });
115
+ }, {
116
+ session: sessionFacts(deps, asString(args['sessionId'])),
117
+ // The AGENT does not wait for the network. `reticle feedback` (a human, at a terminal,
118
+ // watching for an answer) still does — see submitFeedback's `background`.
119
+ background: true,
120
+ });
110
121
  return {
111
122
  ...receipt,
112
- note: receipt.sent
113
- ? 'Filed — thank you. Continue your task; do not retry a failing call just to re-report it.'
114
- : 'NOT sent (see reason). Tell the human what you found so it is not lost.',
123
+ note: receipt.accepted
124
+ ? 'Accepted — thank you. It sends in the background so you are not kept waiting; if it fails you will be told on a later tool result. Continue your task; do not retry a failing call just to re-report it.'
125
+ : 'NOT accepted (see reason). Tell the human what you found so it is not lost.',
115
126
  };
116
127
  },
117
128
  },
@@ -128,8 +139,20 @@ export const FEEDBACK_PROMPT = {
128
139
  ask: 'Ask the human, in your own words and only once: how did this verification go, and what would have made Reticle more useful? If they answer, file it with `reticle feedback` (they run it) or relay it yourself.',
129
140
  command: 'reticle feedback',
130
141
  };
131
- /** Tools whose completion means "a verification just happened" — the only moment worth asking at. */
142
+ /**
143
+ * Tools whose completion means "a verification just happened" — the only moment worth asking at,
144
+ * and the set that decides what `verification_completed` counts.
145
+ *
146
+ * ACT_AND_WAIT belongs here and was missing for a long time. It declares `verified` in its
147
+ * outputSchema, returns the whole verdict block, and its own description calls it "one hop for the
148
+ * act->observe->assert loop" — it IS the verification path most agents take. The guard that should
149
+ * have caught the omission keyed on the tool NAME (/assert|verify/), which act_and_wait does not
150
+ * match, so it slipped through in silence. Measured over a day of real telemetry: act_and_wait 14
151
+ * calls, assert ZERO, verification_completed 2. Agents were verifying the entire time and the metric
152
+ * could not see it. The contract test now checks the SHAPE instead.
153
+ */
132
154
  export const VERIFICATION_TOOLS = new Set([
155
+ ReticleTool.ACT_AND_WAIT,
133
156
  ReticleTool.ASSERT,
134
157
  ReticleTool.FLOW_VERIFY,
135
158
  ReticleTool.VERIFY_CHANGE,
@@ -1,10 +1,4 @@
1
1
  import type { ToolDef, ToolDeps } from './tools.js';
2
- /**
3
- * The live-session tools whose result MUST carry the
4
- * session-health envelope. Owned in ONE place — not retrofitted per handler — so a throttled tab
5
- * can never return a healthy-looking result from any of these. `runTool` is the single choke point
6
- * (mcp.ts + tool-invoker.ts) that splices health on; the guard test asserts the set is exhaustive.
7
- */
8
2
  export declare const SESSION_BOUND_TOOLS: ReadonlySet<string>;
9
3
  /**
10
4
  * Tools that carry a `sessionId` arg but are NOT live-session-health tools — they read/write
@@ -13,10 +7,4 @@ export declare const SESSION_BOUND_TOOLS: ReadonlySet<string>;
13
7
  * `sessionId`-bearing tool to be classified into exactly one set (bound XOR exempt).
14
8
  */
15
9
  export declare const SESSION_EXEMPT_TOOLS: ReadonlySet<string>;
16
- /**
17
- * The single entry point both the MCP server and the programmatic invoker call instead of
18
- * `tool.handler` directly. Runs the handler, then — for a live-session tool returning a plain
19
- * object that did not already include `session` — splices the health envelope on. Idempotent
20
- * (handlers that already add health are left untouched) and never alters non-object results.
21
- */
22
10
  export declare function runTool(tool: ToolDef, deps: ToolDeps, args: Record<string, unknown>): Promise<unknown>;