@reticlehq/server 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) hide show
  1. package/SKILL.md +702 -0
  2. package/dist/bridge/auth-failure-reason.d.ts +19 -0
  3. package/dist/bridge/auth-failure-reason.js +29 -0
  4. package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
  5. package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
  6. package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
  7. package/dist/{bridge.js → bridge/bridge.js} +105 -15
  8. package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
  9. package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
  10. package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
  11. package/dist/capsule/capsule-store.d.ts +8 -3
  12. package/dist/capsule/capsule-store.js +7 -2
  13. package/dist/capsule/capsule.js +3 -3
  14. package/dist/capsule/causal-summary.d.ts +8 -0
  15. package/dist/capsule/causal-summary.js +22 -12
  16. package/dist/capsule/divergence.d.ts +9 -4
  17. package/dist/capsule/divergence.js +8 -8
  18. package/dist/capsule/predicate-to-links.js +11 -8
  19. package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
  20. package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
  21. package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
  22. package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
  23. package/dist/cli/cli-launch.js +171 -0
  24. package/dist/cli/cli-parse.d.ts +143 -0
  25. package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
  26. package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
  27. package/dist/{cli-port.js → cli/cli-port.js} +29 -8
  28. package/dist/cli/cli-update-commands.d.ts +4 -0
  29. package/dist/cli/cli-update-commands.js +93 -0
  30. package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
  31. package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
  32. package/dist/cli.d.ts +2 -2
  33. package/dist/cli.js +182 -93
  34. package/dist/cloud/cloud-config.d.ts +0 -1
  35. package/dist/cloud/cloud-config.js +9 -9
  36. package/dist/cloud/cloud-sync.d.ts +1 -7
  37. package/dist/cloud/cloud-sync.js +12 -12
  38. package/dist/crawl/crawl-empty.d.ts +16 -0
  39. package/dist/crawl/crawl-empty.js +24 -0
  40. package/dist/crawl/crawl-tools.js +4 -1
  41. package/dist/crawl/crawl.js +26 -7
  42. package/dist/daemon/daemon-resilience.d.ts +49 -0
  43. package/dist/daemon/daemon-resilience.js +191 -0
  44. package/dist/daemon/daemon-usefulness.d.ts +61 -0
  45. package/dist/daemon/daemon-usefulness.js +62 -0
  46. package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
  47. package/dist/{daemon.js → daemon/daemon.js} +80 -48
  48. package/dist/daemon/idle-grace.d.ts +32 -0
  49. package/dist/daemon/idle-grace.js +41 -0
  50. package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
  51. package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
  52. package/dist/domain/domain-model.js +2 -2
  53. package/dist/events/body-failures.js +8 -8
  54. package/dist/events/contradictions.d.ts +1 -1
  55. package/dist/events/contradictions.js +60 -10
  56. package/dist/events/echo-mismatch.js +10 -10
  57. package/dist/events/event-filters.js +6 -6
  58. package/dist/events/json-salvage.d.ts +2 -1
  59. package/dist/events/json-salvage.js +6 -6
  60. package/dist/events/observed-in-window.d.ts +12 -0
  61. package/dist/events/observed-in-window.js +21 -0
  62. package/dist/events/predicate-eval.d.ts +43 -20
  63. package/dist/events/predicate-eval.js +159 -55
  64. package/dist/events/predicate.d.ts +2 -2
  65. package/dist/events/predicate.js +112 -35
  66. package/dist/events/reconcile.js +5 -5
  67. package/dist/events/ring-buffer.js +1 -1
  68. package/dist/events/stale-response.js +6 -6
  69. package/dist/events/testid-near-miss.d.ts +22 -0
  70. package/dist/events/testid-near-miss.js +33 -0
  71. package/dist/events/unit-mismatch.js +8 -8
  72. package/dist/flows/affected-tools.js +19 -4
  73. package/dist/flows/affected.js +1 -1
  74. package/dist/flows/annotate-target.d.ts +1 -0
  75. package/dist/flows/annotate-target.js +20 -0
  76. package/dist/flows/annotate-tools.js +25 -3
  77. package/dist/flows/annotate.js +17 -4
  78. package/dist/flows/assertion-integrity.d.ts +2 -1
  79. package/dist/flows/assertion-tiers-store.d.ts +3 -66
  80. package/dist/flows/assertion-tiers-store.js +2 -2
  81. package/dist/flows/attributed-failure.d.ts +17 -0
  82. package/dist/flows/attributed-failure.js +20 -0
  83. package/dist/flows/buddy-status.d.ts +2 -1
  84. package/dist/flows/buddy-status.js +2 -2
  85. package/dist/flows/coverage.d.ts +3 -2
  86. package/dist/flows/coverage.js +2 -2
  87. package/dist/flows/decision.d.ts +0 -6
  88. package/dist/flows/decision.js +64 -8
  89. package/dist/flows/empty-flow.d.ts +6 -0
  90. package/dist/flows/empty-flow.js +24 -0
  91. package/dist/flows/flake-store.d.ts +0 -1
  92. package/dist/flows/flake-store.js +1 -1
  93. package/dist/flows/flake.d.ts +2 -1
  94. package/dist/flows/flake.js +2 -2
  95. package/dist/flows/flow-classify.js +12 -0
  96. package/dist/flows/flow-replay-run.js +4 -4
  97. package/dist/flows/flow-replay.d.ts +5 -7
  98. package/dist/flows/flow-replay.js +114 -47
  99. package/dist/flows/flow-report.js +1 -1
  100. package/dist/flows/flow-scope.d.ts +1 -6
  101. package/dist/flows/flow-step-runners.d.ts +26 -6
  102. package/dist/flows/flow-step-runners.js +157 -6
  103. package/dist/flows/flow-success.js +20 -15
  104. package/dist/flows/flow-tools.js +67 -13
  105. package/dist/flows/flows.js +15 -11
  106. package/dist/flows/gate.d.ts +4 -3
  107. package/dist/flows/gate.js +1 -1
  108. package/dist/flows/heal-run.d.ts +0 -8
  109. package/dist/flows/heal-run.js +5 -5
  110. package/dist/flows/heal.js +3 -3
  111. package/dist/flows/predicate-to-expect.d.ts +38 -0
  112. package/dist/flows/predicate-to-expect.js +95 -0
  113. package/dist/flows/replay.d.ts +16 -1
  114. package/dist/flows/replay.js +58 -22
  115. package/dist/flows/role-anchor-nearest.d.ts +6 -0
  116. package/dist/flows/role-anchor-nearest.js +45 -0
  117. package/dist/flows/server-verify.d.ts +1 -1
  118. package/dist/flows/server-verify.js +5 -5
  119. package/dist/flows/verify-change-tools.js +32 -10
  120. package/dist/flows/verify-change-verdict.d.ts +15 -0
  121. package/dist/flows/verify-change-verdict.js +25 -0
  122. package/dist/flows/watch-batcher.d.ts +3 -2
  123. package/dist/honesty/already-true.d.ts +2 -0
  124. package/dist/honesty/already-true.js +31 -0
  125. package/dist/honesty/blind-spots.d.ts +16 -3
  126. package/dist/honesty/blind-spots.js +24 -12
  127. package/dist/honesty/honesty.d.ts +3 -2
  128. package/dist/honesty/honesty.js +4 -4
  129. package/dist/honesty/uncaptured-bodies.js +1 -1
  130. package/dist/honesty/unread-outcome.js +2 -2
  131. package/dist/honesty/verified.d.ts +22 -3
  132. package/dist/honesty/verified.js +48 -8
  133. package/dist/http-server.js +14 -7
  134. package/dist/hunt/hunt-report.d.ts +2 -1
  135. package/dist/hunt/hunt-report.js +2 -2
  136. package/dist/index.d.ts +8 -6
  137. package/dist/index.js +32 -25
  138. package/dist/init/agent-rules.d.ts +2 -1
  139. package/dist/init/agent-rules.js +45 -7
  140. package/dist/init/app-choice.d.ts +20 -0
  141. package/dist/init/app-choice.js +25 -0
  142. package/dist/init/astro-host.d.ts +21 -0
  143. package/dist/init/astro-host.js +27 -0
  144. package/dist/init/astro-patch.d.ts +16 -0
  145. package/dist/init/astro-patch.js +240 -0
  146. package/dist/init/capabilities.d.ts +17 -0
  147. package/dist/init/capabilities.js +54 -0
  148. package/dist/init/cra.d.ts +37 -0
  149. package/dist/init/cra.js +90 -0
  150. package/dist/init/cursor.js +39 -4
  151. package/dist/init/desktop-doctor.d.ts +3 -2
  152. package/dist/init/desktop-doctor.js +4 -4
  153. package/dist/init/detect.d.ts +26 -0
  154. package/dist/init/detect.js +78 -5
  155. package/dist/init/mcp-registered.d.ts +12 -0
  156. package/dist/init/mcp-registered.js +14 -0
  157. package/dist/init/mcp.d.ts +27 -2
  158. package/dist/init/mcp.js +40 -4
  159. package/dist/init/next-patch.d.ts +39 -0
  160. package/dist/init/next-patch.js +196 -0
  161. package/dist/init/node-io.js +48 -6
  162. package/dist/init/patch-kind.d.ts +20 -0
  163. package/dist/init/patch-kind.js +10 -0
  164. package/dist/init/plan-framework.d.ts +50 -0
  165. package/dist/init/plan-framework.js +341 -0
  166. package/dist/init/plan.d.ts +119 -6
  167. package/dist/init/plan.js +230 -131
  168. package/dist/init/project-id.d.ts +5 -9
  169. package/dist/init/project-id.js +10 -22
  170. package/dist/init/run.d.ts +22 -0
  171. package/dist/init/run.js +393 -28
  172. package/dist/init/slash-command.d.ts +27 -0
  173. package/dist/init/slash-command.js +61 -0
  174. package/dist/init/snippets.d.ts +41 -3
  175. package/dist/init/snippets.js +185 -10
  176. package/dist/init/vite-config.d.ts +4 -10
  177. package/dist/init/vite-config.js +15 -7
  178. package/dist/init/workspace-apps.d.ts +23 -0
  179. package/dist/init/workspace-apps.js +68 -0
  180. package/dist/input/driven-redaction.d.ts +15 -0
  181. package/dist/input/driven-redaction.js +76 -0
  182. package/dist/input/network-detail.d.ts +9 -3
  183. package/dist/input/network-detail.js +43 -23
  184. package/dist/input/network-mock-tools.js +2 -2
  185. package/dist/input/network-mock.js +4 -4
  186. package/dist/input/real-input.js +8 -3
  187. package/dist/input/scroll-find.js +2 -2
  188. package/dist/input/scroll-tools.d.ts +0 -5
  189. package/dist/input/scroll-tools.js +11 -4
  190. package/dist/input/viewport-tools.js +2 -2
  191. package/dist/journal/ambient-store.d.ts +0 -2
  192. package/dist/journal/ambient-store.js +1 -1
  193. package/dist/journal/ambient.js +1 -1
  194. package/dist/journal/attach-journal.d.ts +2 -1
  195. package/dist/journal/deviation-report.js +6 -6
  196. package/dist/journal/deviation-service.js +1 -1
  197. package/dist/journal/envelope-store.d.ts +0 -2
  198. package/dist/journal/envelope-store.js +1 -1
  199. package/dist/journal/envelope.d.ts +2 -3
  200. package/dist/journal/envelope.js +3 -3
  201. package/dist/journal/journal-query.js +1 -1
  202. package/dist/journal/journal-recorder.d.ts +2 -1
  203. package/dist/journal/journal-recorder.js +2 -2
  204. package/dist/journal/rollups.js +9 -9
  205. package/dist/journal/session-end.d.ts +2 -1
  206. package/dist/journal/session-journal.js +4 -4
  207. package/dist/license/license.d.ts +0 -14
  208. package/dist/license/license.js +8 -8
  209. package/dist/log.d.ts +10 -0
  210. package/dist/log.js +11 -1
  211. package/dist/mcp/mcp-is-error.d.ts +13 -0
  212. package/dist/mcp/mcp-is-error.js +18 -0
  213. package/dist/mcp/mcp-outage.d.ts +31 -0
  214. package/dist/mcp/mcp-outage.js +48 -0
  215. package/dist/mcp/mcp-proxy.d.ts +124 -0
  216. package/dist/mcp/mcp-proxy.js +697 -0
  217. package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
  218. package/dist/{mcp.js → mcp/mcp.js} +110 -35
  219. package/dist/mcp/proxy-handshake.d.ts +3 -0
  220. package/dist/mcp/proxy-handshake.js +54 -0
  221. package/dist/mcp/proxy-lifecycle.d.ts +64 -0
  222. package/dist/mcp/proxy-lifecycle.js +70 -0
  223. package/dist/mcp/tool-catalog-cache.d.ts +29 -0
  224. package/dist/mcp/tool-catalog-cache.js +55 -0
  225. package/dist/oracles/propose-consequences.d.ts +2 -1
  226. package/dist/oracles/propose-consequences.js +16 -11
  227. package/dist/oracles/self-instrument.d.ts +2 -1
  228. package/dist/oracles/self-instrument.js +1 -1
  229. package/dist/phenomena/phenomena.js +3 -3
  230. package/dist/platform.d.ts +16 -0
  231. package/dist/platform.js +16 -0
  232. package/dist/pool/playwright-launcher.d.ts +15 -2
  233. package/dist/pool/playwright-launcher.js +21 -2
  234. package/dist/project/fs-port.js +1 -1
  235. package/dist/project/project-tools.js +2 -2
  236. package/dist/runs/build-verification-run.js +1 -1
  237. package/dist/runs/repair-prompt.d.ts +0 -6
  238. package/dist/runs/repair-prompt.js +3 -3
  239. package/dist/runs/risk-classify.js +1 -1
  240. package/dist/runs/run-diff.d.ts +0 -2
  241. package/dist/runs/run-diff.js +3 -3
  242. package/dist/runs/run-tools.js +2 -2
  243. package/dist/runs/verification-sync.js +2 -2
  244. package/dist/runs/verify-http.js +1 -1
  245. package/dist/runs/verify-server.js +1 -1
  246. package/dist/session/command-timeout.d.ts +11 -3
  247. package/dist/session/command-timeout.js +2 -2
  248. package/dist/session/control-envelope.d.ts +6 -0
  249. package/dist/session/control-envelope.js +7 -1
  250. package/dist/session/dev-server-probe.d.ts +13 -0
  251. package/dist/session/dev-server-probe.js +52 -0
  252. package/dist/session/last-act.d.ts +17 -3
  253. package/dist/session/last-act.js +16 -4
  254. package/dist/session/live-control-tools.js +7 -2
  255. package/dist/session/live-control.js +1 -1
  256. package/dist/session/looks-like-dev-server.d.ts +14 -0
  257. package/dist/session/looks-like-dev-server.js +25 -0
  258. package/dist/session/no-session-diagnosis.d.ts +38 -0
  259. package/dist/session/no-session-diagnosis.js +70 -0
  260. package/dist/session/no-session-watch.d.ts +19 -0
  261. package/dist/session/no-session-watch.js +61 -0
  262. package/dist/session/observed-state.d.ts +4 -0
  263. package/dist/session/observed-state.js +19 -1
  264. package/dist/session/output-budget.js +1 -1
  265. package/dist/session/session-health.d.ts +8 -1
  266. package/dist/session/session-health.js +8 -7
  267. package/dist/session/session-info.d.ts +26 -7
  268. package/dist/session/session-info.js +42 -1
  269. package/dist/session/session-lease.d.ts +0 -11
  270. package/dist/session/session-lease.js +1 -1
  271. package/dist/session/session-manager.d.ts +3 -0
  272. package/dist/session/session-manager.js +58 -9
  273. package/dist/session/session-readiness.d.ts +1 -1
  274. package/dist/session/session-readiness.js +6 -1
  275. package/dist/session/session-reaper.js +1 -1
  276. package/dist/session/session-reconnect.d.ts +32 -0
  277. package/dist/session/session-reconnect.js +42 -0
  278. package/dist/session/session.d.ts +20 -2
  279. package/dist/session/session.js +76 -26
  280. package/dist/session/state-select.d.ts +1 -1
  281. package/dist/session/state-select.js +1 -1
  282. package/dist/status-payload.d.ts +19 -0
  283. package/dist/status-payload.js +11 -0
  284. package/dist/telemetry/argument-shape.js +10 -1
  285. package/dist/telemetry/browser-mode.d.ts +27 -0
  286. package/dist/telemetry/browser-mode.js +33 -0
  287. package/dist/telemetry/bug-found.d.ts +17 -21
  288. package/dist/telemetry/bug-found.js +120 -8
  289. package/dist/telemetry/cli-telemetry.d.ts +2 -12
  290. package/dist/telemetry/cli-telemetry.js +27 -4
  291. package/dist/telemetry/daemon-telemetry.d.ts +19 -0
  292. package/dist/telemetry/daemon-telemetry.js +12 -2
  293. package/dist/telemetry/dev-repo.js +4 -4
  294. package/dist/telemetry/error-fingerprint.js +21 -2
  295. package/dist/telemetry/feedback-cli.d.ts +1 -1
  296. package/dist/telemetry/feedback-cli.js +4 -4
  297. package/dist/telemetry/feedback-context.d.ts +22 -0
  298. package/dist/telemetry/feedback-context.js +41 -1
  299. package/dist/telemetry/feedback-delivery.d.ts +20 -0
  300. package/dist/telemetry/feedback-delivery.js +30 -0
  301. package/dist/telemetry/feedback-outbox.d.ts +22 -0
  302. package/dist/telemetry/feedback-outbox.js +87 -0
  303. package/dist/telemetry/feedback.d.ts +22 -1
  304. package/dist/telemetry/feedback.js +67 -8
  305. package/dist/telemetry/git-facts.js +1 -1
  306. package/dist/telemetry/session-metrics.d.ts +2 -0
  307. package/dist/telemetry/session-metrics.js +68 -6
  308. package/dist/telemetry/telemetry.d.ts +27 -2
  309. package/dist/telemetry/telemetry.js +119 -29
  310. package/dist/telemetry/verification-of.d.ts +22 -0
  311. package/dist/telemetry/verification-of.js +84 -0
  312. package/dist/tools/act-capsule.js +2 -2
  313. package/dist/tools/act-danger.d.ts +12 -0
  314. package/dist/tools/act-danger.js +20 -3
  315. package/dist/tools/act-helpers.js +4 -4
  316. package/dist/tools/act-tools.d.ts +0 -8
  317. package/dist/tools/act-tools.js +84 -115
  318. package/dist/tools/act-view.js +7 -7
  319. package/dist/tools/alias-args.d.ts +17 -0
  320. package/dist/tools/alias-args.js +25 -0
  321. package/dist/tools/assert-grade.js +9 -9
  322. package/dist/tools/assert-verdict.d.ts +3 -1
  323. package/dist/tools/assert-verdict.js +8 -5
  324. package/dist/tools/browser-tools.js +30 -11
  325. package/dist/tools/contract-tools.js +1 -1
  326. package/dist/tools/coverage-identity.d.ts +34 -0
  327. package/dist/tools/coverage-identity.js +46 -0
  328. package/dist/tools/coverage-tools.js +10 -14
  329. package/dist/tools/dynamic-tools.d.ts +2 -1
  330. package/dist/tools/dynamic-tools.js +90 -18
  331. package/dist/tools/error-recovery.d.ts +20 -2
  332. package/dist/tools/error-recovery.js +179 -4
  333. package/dist/tools/feedback-tools.d.ts +12 -1
  334. package/dist/tools/feedback-tools.js +28 -5
  335. package/dist/tools/invoke-tool.d.ts +0 -6
  336. package/dist/tools/invoke-tool.js +57 -32
  337. package/dist/tools/lease-hint.d.ts +17 -0
  338. package/dist/tools/lease-hint.js +24 -0
  339. package/dist/tools/lease-tools.js +6 -5
  340. package/dist/tools/merge-tools.d.ts +10 -1
  341. package/dist/tools/merge-tools.js +9 -3
  342. package/dist/tools/merged-name-redirect.d.ts +13 -0
  343. package/dist/tools/merged-name-redirect.js +55 -0
  344. package/dist/tools/navigate-result.d.ts +17 -0
  345. package/dist/tools/navigate-result.js +31 -0
  346. package/dist/tools/observe-tools.js +54 -20
  347. package/dist/tools/observed-nothing.d.ts +25 -0
  348. package/dist/tools/observed-nothing.js +33 -0
  349. package/dist/tools/query-paginate.js +2 -2
  350. package/dist/tools/query-shape.d.ts +18 -0
  351. package/dist/tools/query-shape.js +39 -0
  352. package/dist/tools/read-tools.js +85 -20
  353. package/dist/tools/real-input-attempt.d.ts +28 -0
  354. package/dist/tools/real-input-attempt.js +88 -0
  355. package/dist/tools/reconcile-tools.js +7 -7
  356. package/dist/tools/reload-result.d.ts +14 -0
  357. package/dist/tools/reload-result.js +29 -0
  358. package/dist/tools/snapshot-delta.d.ts +4 -5
  359. package/dist/tools/snapshot-delta.js +0 -0
  360. package/dist/tools/tool-kit.d.ts +8 -0
  361. package/dist/tools/tool-surface.d.ts +83 -0
  362. package/dist/tools/tool-surface.js +204 -0
  363. package/dist/tools/tools-helpers.js +6 -6
  364. package/dist/tools/tools.d.ts +15 -0
  365. package/dist/tools/tools.js +53 -17
  366. package/dist/tools/unadvertised-help.d.ts +5 -0
  367. package/dist/tools/unadvertised-help.js +39 -0
  368. package/dist/trace.d.ts +69 -0
  369. package/dist/trace.js +150 -0
  370. package/dist/update/nudge-credit.d.ts +4 -0
  371. package/dist/update/nudge-credit.js +53 -0
  372. package/dist/update/sdk-sync.d.ts +21 -0
  373. package/dist/update/sdk-sync.js +42 -0
  374. package/dist/update/update-checker.js +3 -3
  375. package/dist/update/update-nudge.d.ts +63 -6
  376. package/dist/update/update-nudge.js +128 -15
  377. package/dist/update/updater.d.ts +1 -9
  378. package/dist/update/updater.js +13 -8
  379. package/dist/version/peer-announce.d.ts +4 -0
  380. package/dist/version/peer-announce.js +30 -0
  381. package/dist/{server-version.js → version/server-version.js} +5 -1
  382. package/dist/version/version-nudge.d.ts +28 -0
  383. package/dist/version/version-nudge.js +40 -0
  384. package/dist/version/version-skew.d.ts +66 -0
  385. package/dist/version/version-skew.js +71 -0
  386. package/dist/visual/visual-diff.js +2 -2
  387. package/dist/visual/visual-tools.js +4 -4
  388. package/docs/agent-cheatsheet.md +85 -0
  389. package/docs/architecture.md +113 -0
  390. package/docs/benchmarks.md +255 -0
  391. package/docs/debugging.md +102 -0
  392. package/docs/deploy-checks.md +69 -0
  393. package/docs/desktop-apps.md +256 -0
  394. package/docs/enterprise.md +62 -0
  395. package/docs/fixtures.md +28 -0
  396. package/docs/flows.md +166 -0
  397. package/docs/getting-started.md +469 -0
  398. package/docs/human-control.md +63 -0
  399. package/docs/integration-patterns.md +171 -0
  400. package/docs/local-registry.md +126 -0
  401. package/docs/mint.json +61 -0
  402. package/docs/multi-agent-testing.md +55 -0
  403. package/docs/platform-integration.md +122 -0
  404. package/docs/telemetry-contract.md +214 -0
  405. package/docs/telemetry.md +184 -0
  406. package/docs/testing.md +82 -0
  407. package/docs/token-efficiency.md +80 -0
  408. package/docs/usage.md +1046 -0
  409. package/package.json +8 -5
  410. package/dist/cli-launch.js +0 -112
  411. package/dist/cli-parse.d.ts +0 -140
  412. package/dist/daemon-resilience.d.ts +0 -22
  413. package/dist/daemon-resilience.js +0 -85
  414. package/dist/mcp-proxy.d.ts +0 -35
  415. package/dist/mcp-proxy.js +0 -190
  416. package/dist/tools/profiles.d.ts +0 -37
  417. package/dist/tools/profiles.js +0 -139
  418. /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
  419. /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
  420. /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
  421. /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
  422. /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
  423. /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
  424. /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
@@ -16,11 +16,11 @@
16
16
  */
17
17
  import { spawn } from 'node:child_process';
18
18
  import { randomUUID, createHash } from 'node:crypto';
19
- import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from 'node:fs';
19
+ import { appendFileSync, readFileSync, writeFileSync, mkdirSync, existsSync, rmSync, } from 'node:fs';
20
20
  import { homedir, platform } from 'node:os';
21
21
  import { join } from 'node:path';
22
- import { TelemetryEventKind, TELEMETRY_EVENT_VERSION, ProjectIdSource, } from '@reticlehq/core';
23
- import { SERVER_VERSION } from '../server-version.js';
22
+ import { TelemetryEventKind, TELEMETRY_EVENT_VERSION, ProjectIdSource, isSessionScoped, } from '@reticlehq/core';
23
+ import { SERVER_VERSION } from '../version/server-version.js';
24
24
  const RETICLE_DIR = join(homedir(), '.reticle');
25
25
  const ID_FILE = join(RETICLE_DIR, 'telemetry-id');
26
26
  const NOTICE_FILE = join(RETICLE_DIR, 'telemetry-notice-shown');
@@ -43,6 +43,22 @@ const POSTHOG_KEY = 'phc_q4yCxKMXHfWQ43VAz4HxJXZFLqFi4nBbg5v3avemggYo';
43
43
  */
44
44
  const POSTHOG_PERSONLESS = { $process_person_profile: false };
45
45
  const SEND_TIMEOUT_MS = 2000;
46
+ /**
47
+ * Feedback gets its own, much larger budget — and a retry.
48
+ *
49
+ * 2s + drop is right for a usage counter: the metric is worthless if it costs the user latency, and
50
+ * losing one changes nothing. Feedback is the opposite on both counts. It is the only qualitative
51
+ * channel the product has, it carries an agent's entire root-cause analysis, and losing one loses
52
+ * work that cannot be reconstructed.
53
+ *
54
+ * Measured to the collector with a WARM DNS cache: dns 0.003s, connect 0.233s, tls 0.467s, total
55
+ * 0.694s — a third of the old budget gone before any payload moves, on the good path. A fresh
56
+ * short-lived CLI process pays cold DNS and a cold route on top, which is exactly the case that
57
+ * failed in the field and then succeeded on retry from a warmer process.
58
+ */
59
+ export const FEEDBACK_TIMEOUT_MS = 15_000;
60
+ export const FEEDBACK_RETRIES = 1;
61
+ export const FEEDBACK_RETRY_BACKOFF_MS = 750;
46
62
  /**
47
63
  * The sender a `detach: true` emit runs in a disowned child (argv: [url, body]) — an in-process fetch
48
64
  * keeps Node's event loop alive until the POST finishes, which taxed every short-lived CLI command
@@ -59,6 +75,7 @@ const Env = {
59
75
  DISABLE: 'RETICLE_TELEMETRY', // "0" / "false" / "off" → disabled
60
76
  DO_NOT_TRACK: 'DO_NOT_TRACK', // the cross-tool opt-out convention (any truthy value)
61
77
  URL: 'RETICLE_TELEMETRY_URL', // override the PostHog host (EU cloud / self-hosted)
78
+ FILE: 'RETICLE_TELEMETRY_FILE', // record to a local JSONL file and send NOTHING — see the sink note
62
79
  KEY: 'RETICLE_TELEMETRY_KEY', // override the PostHog project key
63
80
  CI: 'CI', // presence marks a CI environment
64
81
  VITEST: 'VITEST', // set by vitest — unit tests must never phone home
@@ -67,6 +84,11 @@ const isDisabled = (env, cwd = process.cwd()) => {
67
84
  const off = new Set(['0', 'false', 'off', 'no']);
68
85
  if (off.has((env[Env.DISABLE] ?? '').toLowerCase()))
69
86
  return true;
87
+ // Recording to a local file is not "phoning home", and the guards below exist to stop us phoning
88
+ // home. Chief among them is the source-checkout rule — which is exactly where a release sweep is
89
+ // driven from, so a sink that inherited it would record nothing and look like it had worked.
90
+ if ((env[Env.FILE] ?? '') !== '')
91
+ return false;
70
92
  if (env[Env.VITEST] !== undefined)
71
93
  return true; // a test run is not a user
72
94
  // Developing Reticle is not using Reticle. The `.env` carrying RETICLE_TELEMETRY=0 is gitignored,
@@ -75,8 +97,21 @@ const isDisabled = (env, cwd = process.cwd()) => {
75
97
  if (isReticleSourceCheckout(cwd))
76
98
  return true;
77
99
  const dnt = env[Env.DO_NOT_TRACK];
78
- return typeof dnt === 'string' && dnt !== '' && dnt !== '0';
100
+ return 'string' === typeof dnt && dnt !== '' && dnt !== '0';
79
101
  };
102
+ /**
103
+ * Would telemetry be on here if we ignored the source-checkout guard alone?
104
+ *
105
+ * That guard exists so a fresh clone does not phone home on a contributor's first `reticle serve` —
106
+ * a rule about PASSIVE collection. Feedback is never passive: somebody typed it. Applying the guard
107
+ * to it means anyone dogfooding from their own checkout files nothing and is told "not sent, unknown
108
+ * reason", which silently loses every report from exactly the people most likely to write good ones.
109
+ * Reported from the field, and the reporter only escaped it by happening to run from another repo.
110
+ *
111
+ * Every explicit opt-out still applies — RETICLE_TELEMETRY=0, DO_NOT_TRACK, the persisted opt-out
112
+ * file, and a test run. This lifts one implicit guard for one user-initiated event kind.
113
+ */
114
+ const onlyBlockedBySourceCheckout = (env, cwd) => isDisabled(env, cwd) && !isDisabled(env, '/');
80
115
  /** Read (or mint-and-persist) the anonymous machine id. `firstRun` is true the run that created it. */
81
116
  const resolveIdentity = () => {
82
117
  try {
@@ -172,7 +207,8 @@ export const setTelemetryEnabled = (enabled, dir = RETICLE_DIR) => {
172
207
  mkdirSync(dir, { recursive: true });
173
208
  writeFileSync(optOutFile, '1', 'utf8');
174
209
  };
175
- const NOOP = { emit: async () => { }, enabled: false, firstRun: false };
210
+ // A disabled emitter delivers nothing, and says so — `false` is the honest answer, not a courtesy.
211
+ const NOOP = { emit: () => Promise.resolve(false), enabled: false, firstRun: false };
176
212
  /**
177
213
  * Build the telemetry emitter for this process. Resolves identity + opt-out ONCE; each `emit` builds a
178
214
  * fully-validated core event, maps it into PostHog capture shape, and fires a best-effort POST.
@@ -182,7 +218,9 @@ const NOOP = { emit: async () => { }, enabled: false, firstRun: false };
182
218
  export const createTelemetry = (opts) => {
183
219
  const env = opts.env ?? process.env;
184
220
  const cwd = opts.cwd ?? process.cwd();
185
- if (isDisabled(env, cwd))
221
+ // A source checkout silences metrics but must not silence FEEDBACK — see onlyBlockedBySourceCheckout.
222
+ const feedbackOnly = onlyBlockedBySourceCheckout(env, cwd);
223
+ if (isDisabled(env, cwd) && !feedbackOnly)
186
224
  return NOOP;
187
225
  try {
188
226
  if (existsSync(OPT_OUT_FILE))
@@ -192,13 +230,15 @@ export const createTelemetry = (opts) => {
192
230
  /* unreadable home dir — fall through; the env-var opt-outs above still apply */
193
231
  }
194
232
  const apiKey = env[Env.KEY] ?? POSTHOG_KEY;
195
- if (apiKey === '')
233
+ if ('' === apiKey)
196
234
  return NOOP; // no key baked in (dev/test build) — nowhere to send, stay silent
197
235
  const now = opts.now ?? (() => Date.now());
198
236
  const doFetch = opts.fetchImpl ?? fetch;
199
237
  const url = `${(env[Env.URL] ?? DEFAULT_URL).replace(/\/+$/, '')}${TELEMETRY_PATH}`;
200
238
  const ci = env[Env.CI] !== undefined && env[Env.CI] !== '';
201
239
  const os = platform();
240
+ // Negated so it reads the way people say it: UTC+2 is +120, not -120.
241
+ const tzOffsetMin = -new Date().getTimezoneOffset();
202
242
  const { anonymousId, firstRun } = resolveIdentity();
203
243
  /**
204
244
  * One id per PROCESS, minted in memory and never persisted — which is exactly right: a daemon run
@@ -212,7 +252,22 @@ export const createTelemetry = (opts) => {
212
252
  ((command, args) => {
213
253
  spawn(command, args, { detached: true, stdio: 'ignore' }).unref();
214
254
  });
255
+ /**
256
+ * Returns whether the event was actually DELIVERED.
257
+ *
258
+ * Every caller but one ignores this and should: a lost metric must never surface to a user. The
259
+ * exception is `reticle_feedback`, which hands a receipt back to an agent — `sent: true` there was
260
+ * unconditional, so a DNS miss or a 4xx both read as "filed", and the only qualitative channel the
261
+ * product has could fail silently while telling the reporter it had worked.
262
+ *
263
+ * A detached send reports false: it is handed to a disowned child precisely so the caller does not
264
+ * wait, so its outcome is genuinely unknown and claiming success would be the same lie.
265
+ */
215
266
  const emit = async (kind, extra) => {
267
+ // In a Reticle source checkout the emitter carries FEEDBACK and nothing else: somebody typed the
268
+ // feedback, and no metric is worth phoning home from a contributor's clone.
269
+ if (feedbackOnly && TelemetryEventKind.FEEDBACK_SUBMITTED !== kind)
270
+ return false;
216
271
  const event = {
217
272
  v: TELEMETRY_EVENT_VERSION,
218
273
  anonymousId,
@@ -223,6 +278,7 @@ export const createTelemetry = (opts) => {
223
278
  version: opts.version,
224
279
  ci,
225
280
  os,
281
+ tzOffsetMin,
226
282
  projectIdSource,
227
283
  ...(extra?.actor !== undefined ? { actor: extra.actor } : {}),
228
284
  ...(extra?.command !== undefined ? { command: extra.command } : {}),
@@ -237,19 +293,22 @@ export const createTelemetry = (opts) => {
237
293
  ...(extra?.connection !== undefined ? { connection: extra.connection } : {}),
238
294
  ...(extra?.init !== undefined ? { init: extra.init } : {}),
239
295
  ...(extra?.bug !== undefined ? { bug: extra.bug } : {}),
296
+ ...(extra?.outage !== undefined ? { outage: extra.outage } : {}),
240
297
  };
241
298
  // Map the core contract onto PostHog's capture shape: id/name/time move up, the rest are properties.
242
299
  // The feedback body is FLATTENED into `feedback_*` properties rather than sent as a nested object:
243
300
  // PostHog filters and breakdowns operate on top-level properties, so a nested `feedback.stack`
244
301
  // would be invisible to the exact "which stacks report the most bugs" question this exists to
245
302
  // answer. One prefix keeps them grouped in the UI without a nested path.
246
- const { anonymousId: distinctId, event: name, ts, sessionId: eventSessionId, feedback, session, project, verification, versionChange, crash, identity, connection, init, bug, ...rest } = event;
303
+ const { anonymousId: distinctId, event: name, ts, sessionId: eventSessionId, feedback, session, project, verification, versionChange, crash, identity, connection, init, bug, outage, ...rest } = event;
247
304
  // `$session_id` is PostHog's OWN session property, so sending ours under that name lights up
248
305
  // its built-in session views and funnels for free rather than requiring custom HogQL everywhere.
306
+ // `sessionId` rides only on events that happened inside a daemon run. A one-shot CLI command
307
+ // mints a per-process id that joins to nothing and inflates every session count — see
308
+ // isSessionScoped for the measurement.
249
309
  const properties = {
250
310
  ...rest,
251
- sessionId: eventSessionId,
252
- $session_id: eventSessionId,
311
+ ...(isSessionScoped(name) ? { sessionId: eventSessionId, $session_id: eventSessionId } : {}),
253
312
  };
254
313
  // Each block is flattened under its own prefix rather than nested. PostHog filters, breakdowns and
255
314
  // insight builders all operate on TOP-LEVEL properties, so a nested `project.stack` is invisible to
@@ -269,37 +328,68 @@ export const createTelemetry = (opts) => {
269
328
  connection,
270
329
  init,
271
330
  bug,
331
+ outage,
272
332
  };
273
333
  for (const [prefix, block] of Object.entries(blocks)) {
274
334
  for (const [key, value] of Object.entries(block ?? {})) {
275
335
  properties[`${prefix}_${key}`] = value;
276
336
  }
277
337
  }
278
- const body = JSON.stringify({
279
- api_key: apiKey,
280
- batch: [
281
- {
282
- event: name,
283
- distinct_id: distinctId,
284
- timestamp: new Date(ts).toISOString(),
285
- properties: { ...properties, ...POSTHOG_PERSONLESS },
286
- },
287
- ],
288
- });
338
+ const capture = {
339
+ event: name,
340
+ distinct_id: distinctId,
341
+ timestamp: new Date(ts).toISOString(),
342
+ properties: { ...properties, ...POSTHOG_PERSONLESS },
343
+ };
344
+ const body = JSON.stringify({ api_key: apiKey, batch: [capture] });
345
+ const filePath = env[Env.FILE];
346
+ if (filePath !== undefined && filePath !== '') {
347
+ // The SAME payload the wire would have carried, built by the same code path and redacted by
348
+ // the same rules — so what a run records is what a user would have sent. One JSON object per
349
+ // line, appended, so a whole sweep lands in one file in order.
350
+ try {
351
+ appendFileSync(filePath, `${JSON.stringify(capture)}\n`, 'utf8');
352
+ return true;
353
+ }
354
+ catch {
355
+ return false; // a sink that cannot write must not take the daemon down
356
+ }
357
+ }
289
358
  try {
290
- if (extra?.detach === true && opts.fetchImpl === undefined) {
359
+ if (true === extra?.detach && opts.fetchImpl === undefined) {
291
360
  spawnDetached(process.execPath, ['-e', DETACHED_SEND_SCRIPT, url, body]);
292
- return;
361
+ return false; // handed off, outcome unknowable from here
362
+ }
363
+ // Feedback is slower and retried; everything else keeps the fire-and-forget budget.
364
+ const isFeedback = TelemetryEventKind.FEEDBACK_SUBMITTED === kind;
365
+ const timeoutMs = isFeedback ? FEEDBACK_TIMEOUT_MS : SEND_TIMEOUT_MS;
366
+ const attempts = 1 + (isFeedback ? FEEDBACK_RETRIES : 0);
367
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
368
+ if (0 < attempt) {
369
+ await new Promise((r) => setTimeout(r, FEEDBACK_RETRY_BACKOFF_MS * attempt));
370
+ }
371
+ try {
372
+ const response = await doFetch(url, {
373
+ method: 'POST',
374
+ headers: { 'content-type': 'application/json' },
375
+ body,
376
+ signal: AbortSignal.timeout(timeoutMs),
377
+ });
378
+ // A 4xx is a rejected payload, not a delivery. It must not read as filed. It is also not
379
+ // worth retrying — the payload will be rejected again — so only a THROW (timeout, DNS,
380
+ // connection reset) goes round again.
381
+ return response.ok !== false;
382
+ }
383
+ catch (error) {
384
+ if (attempt === attempts - 1)
385
+ throw error;
386
+ }
293
387
  }
294
- await doFetch(url, {
295
- method: 'POST',
296
- headers: { 'content-type': 'application/json' },
297
- body,
298
- signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
299
- });
388
+ return false;
300
389
  }
301
390
  catch {
302
391
  /* best-effort: a lost metric must never surface to the user */
392
+ return false;
303
393
  }
304
394
  };
305
395
  return { emit, enabled: true, firstRun };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Which tool results count as a completed verification, as one pure rule.
3
+ *
4
+ * It used to live inline in `runTool` and read `status === 'pass'`, mapping everything else to
5
+ * "no verdict". That was wrong in both directions at once, and the shape of the error is visible in
6
+ * the production data:
7
+ *
8
+ * - a FAILING suite emitted nothing, so `verification_completed` only ever fired on greens while
9
+ * `bugsInResult` fired on the reds — bugs with no verifications to divide them by;
10
+ * - an EMPTY suite ("all 0 flows pass") emitted `verified: yes, passed: true`.
11
+ *
12
+ * Extracted because a rule this easy to get wrong, feeding the one number shown to investors, should
13
+ * be readable and testable on its own rather than inferred from a ternary inside a dispatcher.
14
+ */
15
+ import { type BrowserBrand, type Verification } from '@reticlehq/core';
16
+ /**
17
+ * The verification payload for a result, or undefined when nothing was verified.
18
+ *
19
+ * `falseGreenCaught` is the one that matters: the assertion PASSED and Reticle still refused to call
20
+ * it verified. That is the product's thesis reduced to a boolean.
21
+ */
22
+ export declare function verificationOf(toolName: string, result: Record<string, unknown>, durationMs: number, brand?: BrowserBrand): Verification | undefined;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Which tool results count as a completed verification, as one pure rule.
3
+ *
4
+ * It used to live inline in `runTool` and read `status === 'pass'`, mapping everything else to
5
+ * "no verdict". That was wrong in both directions at once, and the shape of the error is visible in
6
+ * the production data:
7
+ *
8
+ * - a FAILING suite emitted nothing, so `verification_completed` only ever fired on greens while
9
+ * `bugsInResult` fired on the reds — bugs with no verifications to divide them by;
10
+ * - an EMPTY suite ("all 0 flows pass") emitted `verified: yes, passed: true`.
11
+ *
12
+ * Extracted because a rule this easy to get wrong, feeding the one number shown to investors, should
13
+ * be readable and testable on its own rather than inferred from a ternary inside a dispatcher.
14
+ */
15
+ import { VerifiedReason } from '@reticlehq/core';
16
+ import { VERIFICATION_TOOLS } from '../tools/feedback-tools.js';
17
+ import { getBrowserMode } from './browser-mode.js';
18
+ /**
19
+ * The deciding clause, read off the result `decideVerified` wrote it onto.
20
+ *
21
+ * Narrowed against core's own list rather than forwarded: a result is an untyped record here, and a
22
+ * value that is not a member is not a reason — absent beats a string nobody can group by. Derived
23
+ * from the enum, never re-listed, so a new clause is covered the day it exists.
24
+ */
25
+ const REASONS = new Set(Object.values(VerifiedReason));
26
+ function reasonOf(result) {
27
+ const raw = result['verifiedReason'];
28
+ return 'string' === typeof raw && REASONS.has(raw) ? raw : undefined;
29
+ }
30
+ /** Suite statuses. `unverifiable` is a suite that ran but proved nothing — including an empty one. */
31
+ const SuiteStatus = {
32
+ PASS: 'pass',
33
+ FAIL: 'fail',
34
+ };
35
+ /**
36
+ * The verification payload for a result, or undefined when nothing was verified.
37
+ *
38
+ * `falseGreenCaught` is the one that matters: the assertion PASSED and Reticle still refused to call
39
+ * it verified. That is the product's thesis reduced to a boolean.
40
+ */
41
+ export function verificationOf(toolName, result, durationMs, brand) {
42
+ if (!VERIFICATION_TOOLS.has(toolName))
43
+ return undefined;
44
+ // A paused session REFUSES the call — nothing driven, nothing asserted. The refusal carries a
45
+ // verdict field so the agent never reads undefined off it, and that field must not be mistaken
46
+ // here for work that happened.
47
+ if (true === result['paused'])
48
+ return undefined;
49
+ const verified = 'string' === typeof result['verified'] ? result['verified'] : undefined;
50
+ // flow_verify reports `status: pass|fail|unverifiable`; assert reports a boolean `pass`. Accept
51
+ // either shape so the whole family is covered without normalizing four tools' contracts for a
52
+ // metric's convenience — but a status that is neither pass nor fail is NOT a verdict, and an empty
53
+ // suite reports exactly that.
54
+ const status = result['status'];
55
+ const passed = 'boolean' === typeof result['pass']
56
+ ? result['pass']
57
+ : status === SuiteStatus.PASS
58
+ ? true
59
+ : status === SuiteStatus.FAIL
60
+ ? false
61
+ : undefined;
62
+ if (verified === undefined && passed === undefined)
63
+ return undefined;
64
+ const reason = reasonOf(result);
65
+ return {
66
+ via: toolName,
67
+ verified: verified ?? (true === passed ? 'yes' : 'no'),
68
+ passed: passed ?? 'yes' === verified,
69
+ falseGreenCaught: true === passed && 'no' === verified,
70
+ durationMs,
71
+ // Headless CI, a human watching, or somebody's own dev server — three different products behind
72
+ // one number until this was recorded.
73
+ browser: getBrowserMode(),
74
+ // WHICH browser, when the page said. `attached` is the common case and says only that Reticle
75
+ // launched nothing; the brand is the difference between "somebody's own browser" and knowing it
76
+ // was Edge. Omitted when unreported — a desktop webview and an older SDK both have no answer,
77
+ // and "unknown" would be a value on a dashboard rather than a gap.
78
+ ...(brand !== undefined ? { brand } : {}),
79
+ // WHY, not just what. `verified: unknown` covered seven different clauses of the honesty rule
80
+ // belonging to three different owners — the agent, the app, and Reticle's own blind spots — and
81
+ // they need opposite responses. A suite verdict has no clause behind it and reports none.
82
+ ...(reason === undefined ? {} : { reason }),
83
+ };
84
+ }
@@ -10,7 +10,7 @@
10
10
  * must never fail the run that found it.
11
11
  */
12
12
  import { ActionType, AnchorKind } from '@reticlehq/core';
13
- import { CapsuleStore, capsuleId } from '../capsule/capsule-store.js';
13
+ import { CapsuleStore, capsuleId, CAPSULE_VERSION } from '../capsule/capsule-store.js';
14
14
  import { ReticleTool } from './tool-names.js';
15
15
  import { asRecord, asString } from './tools-helpers.js';
16
16
  /** Returns the capsule id when one was written, or undefined when there was nothing to save. */
@@ -23,7 +23,7 @@ export async function saveFailedAssertCapsule(inputs) {
23
23
  .map((l) => ('name' in l ? `${l.kind} ${String(l.name)}` : l.kind))
24
24
  .join(' AND ');
25
25
  const saved = await new CapsuleStore(deps.fs, deps.reticleRoot).save({
26
- version: 1,
26
+ version: CAPSULE_VERSION,
27
27
  id,
28
28
  createdAt: deps.now(),
29
29
  origin: 'failed-assert',
@@ -5,3 +5,15 @@ export declare function assertNotDestructive(action: ActionType, innerArgs: Reco
5
5
  * source alone never says so.
6
6
  */
7
7
  export declare function assertDragNotDestructive(innerArgs: Record<string, unknown>, from: unknown, to: unknown): void;
8
+ /**
9
+ * `reticle_act_and_wait` cannot drive native input, and used to take `args.native` and ignore it.
10
+ *
11
+ * An open `args` passthrough accepted the field, the handler drove the page through the SDK anyway,
12
+ * and the result claimed success — so an agent asking for the one thing a synthetic click cannot do
13
+ * (a file picker, the clipboard, an `isTrusted`-gated handler) got a synthetic click and no hint
14
+ * that its request had been dropped. A silently ignored argument is a false promise; refusing with
15
+ * the route that DOES work is the honest answer.
16
+ */
17
+ export declare const NATIVE_INPUT_UNSUPPORTED: string;
18
+ /** Refuse rather than silently drop a native-input request the act-then-wait path cannot honour. */
19
+ export declare function assertNativeInputSupported(innerArgs: Record<string, unknown>): void;
@@ -1,4 +1,4 @@
1
- import { ActionType, DANGEROUS_ACTION_CONFIRM_ARG, isDangerousActionText } from '@reticlehq/core';
1
+ import { ActionType, DANGEROUS_ACTION_CONFIRM_ARG, NATIVE_INPUT_ARG, isDangerousActionText, } from '@reticlehq/core';
2
2
  import { asRecord, asString } from './tools-helpers.js';
3
3
  /**
4
4
  * Refuse to drive a money-moving or destructive control by accident.
@@ -25,7 +25,7 @@ function descriptorText(value) {
25
25
  export function assertNotDestructive(action, innerArgs, inspected) {
26
26
  if (action !== ActionType.CLICK && action !== ActionType.DBLCLICK)
27
27
  return;
28
- if (innerArgs[DANGEROUS_ACTION_CONFIRM_ARG] === true)
28
+ if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
29
29
  return;
30
30
  if (!isDangerousActionText(descriptorText(inspected)))
31
31
  return;
@@ -36,9 +36,26 @@ export function assertNotDestructive(action, innerArgs, inspected) {
36
36
  * source alone never says so.
37
37
  */
38
38
  export function assertDragNotDestructive(innerArgs, from, to) {
39
- if (innerArgs[DANGEROUS_ACTION_CONFIRM_ARG] === true)
39
+ if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
40
40
  return;
41
41
  if (!isDangerousActionText(`${descriptorText(from)} ${descriptorText(to)}`))
42
42
  return;
43
43
  throw new Error(`potentially destructive native action blocked; retry with args.${DANGEROUS_ACTION_CONFIRM_ARG}=true`);
44
44
  }
45
+ /**
46
+ * `reticle_act_and_wait` cannot drive native input, and used to take `args.native` and ignore it.
47
+ *
48
+ * An open `args` passthrough accepted the field, the handler drove the page through the SDK anyway,
49
+ * and the result claimed success — so an agent asking for the one thing a synthetic click cannot do
50
+ * (a file picker, the clipboard, an `isTrusted`-gated handler) got a synthetic click and no hint
51
+ * that its request had been dropped. A silently ignored argument is a false promise; refusing with
52
+ * the route that DOES work is the honest answer.
53
+ */
54
+ export const NATIVE_INPUT_UNSUPPORTED = `reticle_act_and_wait cannot drive native input, so args.${NATIVE_INPUT_ARG} would be ignored. ` +
55
+ `Use reticle_act { args: { ${NATIVE_INPUT_ARG}: true } } for the trusted click, then assert the ` +
56
+ 'consequence with reticle_assert / reticle_observe using the `since` cursor it returns.';
57
+ /** Refuse rather than silently drop a native-input request the act-then-wait path cannot honour. */
58
+ export function assertNativeInputSupported(innerArgs) {
59
+ if (true === innerArgs[NATIVE_INPUT_ARG])
60
+ throw new Error(NATIVE_INPUT_UNSUPPORTED);
61
+ }
@@ -5,16 +5,16 @@
5
5
  * session, a command, or a tool definition — these are total functions over their arguments, which
6
6
  * is why they can be tested without a browser and why they were the right thing to move.
7
7
  */
8
- import { ActionType } from '@reticlehq/core';
8
+ import { ActionType, ConsequenceKind } from '@reticlehq/core';
9
9
  import { HonestyGrade } from '../honesty/honesty.js';
10
10
  import { asNumber, asRecord, asString } from './tools-helpers.js';
11
11
  /** The strongest consequence grade a set of expected links proves (signal > net > state > presence). */
12
12
  export function gradeOf(links) {
13
- if (links.some((l) => l.kind === 'signal'))
13
+ if (links.some((l) => ConsequenceKind.SIGNAL === l.kind))
14
14
  return HonestyGrade.SIGNAL;
15
- if (links.some((l) => l.kind === 'net'))
15
+ if (links.some((l) => ConsequenceKind.NET === l.kind))
16
16
  return HonestyGrade.NET;
17
- if (links.some((l) => l.kind === 'state'))
17
+ if (links.some((l) => ConsequenceKind.STATE === l.kind))
18
18
  return HonestyGrade.STATE;
19
19
  return HonestyGrade.PRESENCE;
20
20
  }
@@ -1,10 +1,2 @@
1
1
  import { type ToolDef } from './tool-kit.js';
2
- /**
3
- * Narrow the wire's `action` to a real ActionType, or undefined.
4
- *
5
- * It used to be `asString(args['action']) ?? ''`, so an unknown or missing action became the empty
6
- * string and travelled on — reaching the browser as a command it could not perform, and reported back
7
- * as a generic failure rather than "that is not an action". Validate at the boundary, per the project's
8
- * unknown-plus-narrowing rule, so a typo is rejected where it can still be explained.
9
- */
10
2
  export declare const ACT_TOOLS: ToolDef[];