@reticlehq/server 2.11.0 → 2.13.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 (513) hide show
  1. package/SKILL.md +93 -111
  2. package/dist/bridge/auth-failure-reason.d.ts +6 -2
  3. package/dist/bridge/auth-failure-reason.js +15 -8
  4. package/dist/bridge/bridge.d.ts +18 -14
  5. package/dist/bridge/bridge.js +248 -165
  6. package/dist/bridge/bridge.test-harness.d.ts +2 -0
  7. package/dist/bridge/bridge.test-harness.js +8 -1
  8. package/dist/bridge/pairing-token.d.ts +5 -0
  9. package/dist/bridge/pairing-token.js +28 -0
  10. package/dist/bridge/protocol-skew.d.ts +37 -0
  11. package/dist/bridge/protocol-skew.js +45 -0
  12. package/dist/capsule/capsule-store.d.ts +8 -7
  13. package/dist/capsule/capsule-store.js +1 -1
  14. package/dist/capsule/causal-summary.d.ts +2 -2
  15. package/dist/capsule/causal-summary.js +7 -2
  16. package/dist/chromium-launch-options.d.ts +28 -0
  17. package/dist/chromium-launch-options.js +29 -0
  18. package/dist/cli/cli-doctor.js +39 -1
  19. package/dist/cli/cli-flow-commands.d.ts +3 -1
  20. package/dist/cli/cli-flow-commands.js +34 -4
  21. package/dist/cli/cli-kill.d.ts +2 -1
  22. package/dist/cli/cli-launch.d.ts +11 -1
  23. package/dist/cli/cli-launch.js +59 -10
  24. package/dist/cli/cli-parse.d.ts +17 -1
  25. package/dist/cli/cli-parse.js +238 -12
  26. package/dist/cli/cli-update-commands.js +20 -1
  27. package/dist/cli/cli-verify.d.ts +2 -1
  28. package/dist/cli/cli-verify.js +5 -4
  29. package/dist/cli/cloud-cli.js +428 -253
  30. package/dist/cli/cloud-keystore.d.ts +37 -0
  31. package/dist/cli/cloud-keystore.js +97 -0
  32. package/dist/cli/cloud-kit.d.ts +37 -0
  33. package/dist/cli/cloud-kit.js +121 -0
  34. package/dist/cli/cloud-login.d.ts +17 -0
  35. package/dist/cli/cloud-login.js +183 -0
  36. package/dist/cli/cloud-session.d.ts +48 -0
  37. package/dist/cli/cloud-session.js +53 -0
  38. package/dist/cli/config-discovery.d.ts +2 -1
  39. package/dist/cli/config-discovery.js +1 -0
  40. package/dist/cli/daemon-start-options.d.ts +32 -0
  41. package/dist/cli/daemon-start-options.js +38 -0
  42. package/dist/cli/device-flow.d.ts +76 -0
  43. package/dist/cli/device-flow.js +56 -0
  44. package/dist/cli/doctor-rows.d.ts +12 -1
  45. package/dist/cli/doctor-rows.js +12 -1
  46. package/dist/cli/drive-attach.d.ts +13 -0
  47. package/dist/cli/drive-attach.js +18 -0
  48. package/dist/cli/drive-command.js +7 -3
  49. package/dist/cli/gate-exit.d.ts +21 -0
  50. package/dist/cli/gate-exit.js +21 -0
  51. package/dist/cli/gate-hook-message.d.ts +33 -0
  52. package/dist/cli/gate-hook-message.js +46 -0
  53. package/dist/cli/mcp-command.d.ts +25 -0
  54. package/dist/cli/mcp-command.js +174 -0
  55. package/dist/cli/project-name.d.ts +30 -0
  56. package/dist/cli/project-name.js +44 -0
  57. package/dist/cli/sibling-ports.d.ts +37 -0
  58. package/dist/cli/sibling-ports.js +56 -0
  59. package/dist/cli/status-next-action.d.ts +14 -0
  60. package/dist/cli/status-next-action.js +4 -4
  61. package/dist/cli.d.ts +1 -0
  62. package/dist/cli.js +167 -186
  63. package/dist/cloud/cloud-config.d.ts +24 -2
  64. package/dist/cloud/cloud-config.js +64 -5
  65. package/dist/cloud/cloud-sync.d.ts +16 -5
  66. package/dist/cloud/cloud-sync.js +64 -1
  67. package/dist/cloud/sync-cycle.d.ts +110 -0
  68. package/dist/cloud/sync-cycle.js +270 -0
  69. package/dist/cloud/sync-daemon.d.ts +69 -0
  70. package/dist/cloud/sync-daemon.js +260 -0
  71. package/dist/cloud/sync-disk.d.ts +9 -0
  72. package/dist/cloud/sync-disk.js +191 -0
  73. package/dist/cloud/sync-hash.d.ts +2 -0
  74. package/dist/cloud/sync-hash.js +35 -0
  75. package/dist/crawl/crawl-tools.js +5 -3
  76. package/dist/crawl/crawl.d.ts +12 -13
  77. package/dist/crawl/crawl.js +25 -3
  78. package/dist/daemon/daemon-resolve.d.ts +122 -0
  79. package/dist/daemon/daemon-resolve.js +187 -0
  80. package/dist/daemon/dev-servers.d.ts +15 -0
  81. package/dist/daemon/dev-servers.js +68 -0
  82. package/dist/daemon/free-port.d.ts +29 -0
  83. package/dist/daemon/free-port.js +55 -0
  84. package/dist/daemon/heartbeat.d.ts +0 -4
  85. package/dist/daemon/heartbeat.js +2 -2
  86. package/dist/daemon/wake-decision.d.ts +32 -0
  87. package/dist/daemon/wake-decision.js +38 -0
  88. package/dist/dev/stale-issue-guard.d.ts +0 -2
  89. package/dist/dev/stale-issue-guard.js +1 -1
  90. package/dist/domain/domain-tools.js +2 -2
  91. package/dist/events/contradictions.d.ts +59 -0
  92. package/dist/events/contradictions.js +253 -27
  93. package/dist/events/declared.d.ts +17 -0
  94. package/dist/events/declared.js +43 -1
  95. package/dist/events/echo-mismatch.js +65 -1
  96. package/dist/events/event-filters.d.ts +8 -0
  97. package/dist/events/event-filters.js +19 -2
  98. package/dist/events/observed-in-window.d.ts +12 -0
  99. package/dist/events/observed-in-window.js +17 -0
  100. package/dist/events/predicate-asks.d.ts +13 -0
  101. package/dist/events/predicate-asks.js +22 -0
  102. package/dist/events/predicate-eval.js +117 -27
  103. package/dist/events/predicate-parse.d.ts +0 -6
  104. package/dist/events/predicate-parse.js +43 -3
  105. package/dist/events/predicate-precheck.d.ts +8 -0
  106. package/dist/events/predicate-precheck.js +41 -0
  107. package/dist/events/predicate-route.d.ts +14 -0
  108. package/dist/events/predicate-route.js +36 -8
  109. package/dist/events/predicate-schema.d.ts +44 -0
  110. package/dist/events/predicate-schema.js +126 -23
  111. package/dist/events/predicate.d.ts +20 -0
  112. package/dist/events/predicate.js +77 -6
  113. package/dist/events/split-text-miss.d.ts +30 -0
  114. package/dist/events/split-text-miss.js +44 -0
  115. package/dist/flows/coverage.d.ts +0 -8
  116. package/dist/flows/coverage.js +1 -1
  117. package/dist/flows/decision.d.ts +5 -1
  118. package/dist/flows/decision.js +33 -7
  119. package/dist/flows/flake.d.ts +6 -8
  120. package/dist/flows/flake.js +1 -1
  121. package/dist/flows/flow-intent.d.ts +52 -0
  122. package/dist/flows/flow-intent.js +123 -0
  123. package/dist/flows/flow-memory-consult.d.ts +55 -0
  124. package/dist/flows/flow-memory-consult.js +38 -0
  125. package/dist/flows/flow-replay-run.d.ts +18 -7
  126. package/dist/flows/flow-replay-run.js +263 -23
  127. package/dist/flows/flow-replay.js +11 -2
  128. package/dist/flows/flow-step-runners.d.ts +8 -0
  129. package/dist/flows/flow-step-runners.js +8 -2
  130. package/dist/flows/flow-store-for-session.d.ts +31 -0
  131. package/dist/flows/flow-store-for-session.js +33 -0
  132. package/dist/flows/flow-success.js +15 -3
  133. package/dist/flows/flow-tools.js +87 -33
  134. package/dist/flows/flows.d.ts +14 -1
  135. package/dist/flows/flows.js +502 -24
  136. package/dist/flows/heal-run.js +5 -2
  137. package/dist/flows/predicate-to-expect.d.ts +17 -12
  138. package/dist/flows/predicate-to-expect.js +36 -15
  139. package/dist/flows/recordings.d.ts +13 -1
  140. package/dist/flows/recordings.js +17 -2
  141. package/dist/flows/replay.d.ts +22 -2
  142. package/dist/flows/replay.js +28 -2
  143. package/dist/flows/server-verify.js +16 -2
  144. package/dist/flows/verify-change-tools.js +9 -3
  145. package/dist/honesty/blind-spots.d.ts +51 -15
  146. package/dist/honesty/blind-spots.js +64 -4
  147. package/dist/honesty/feature-capture.d.ts +150 -0
  148. package/dist/honesty/feature-capture.js +233 -0
  149. package/dist/honesty/honesty.d.ts +24 -0
  150. package/dist/honesty/honesty.js +25 -1
  151. package/dist/honesty/instrumentation-gaps.d.ts +39 -6
  152. package/dist/honesty/instrumentation-gaps.js +88 -5
  153. package/dist/honesty/tool-hit-rate.d.ts +86 -0
  154. package/dist/honesty/tool-hit-rate.js +90 -0
  155. package/dist/honesty/undeclared-change.d.ts +51 -0
  156. package/dist/honesty/undeclared-change.js +56 -0
  157. package/dist/honesty/verified.d.ts +16 -0
  158. package/dist/honesty/verified.js +84 -5
  159. package/dist/http-server.d.ts +0 -1
  160. package/dist/http-server.js +13 -1
  161. package/dist/hunt/hunt-report.d.ts +0 -2
  162. package/dist/hunt/hunt-report.js +1 -1
  163. package/dist/impact/impact-recorder.d.ts +31 -8
  164. package/dist/impact/impact-recorder.js +104 -21
  165. package/dist/impact/impact-store.d.ts +13 -4
  166. package/dist/impact/impact-store.js +44 -3
  167. package/dist/index.d.ts +4 -1
  168. package/dist/index.js +113 -7
  169. package/dist/init/agent-rules.d.ts +30 -6
  170. package/dist/init/agent-rules.js +54 -13
  171. package/dist/init/astro-patch.d.ts +16 -0
  172. package/dist/init/astro-patch.js +28 -0
  173. package/dist/init/capabilities.d.ts +4 -3
  174. package/dist/init/capabilities.js +23 -3
  175. package/dist/init/closing-hint.d.ts +36 -0
  176. package/dist/init/closing-hint.js +120 -0
  177. package/dist/init/confirm.d.ts +27 -28
  178. package/dist/init/confirm.js +51 -11
  179. package/dist/init/cra.d.ts +34 -5
  180. package/dist/init/cra.js +96 -25
  181. package/dist/init/csp-check.js +17 -2
  182. package/dist/init/csp-doctor.d.ts +8 -1
  183. package/dist/init/csp-doctor.js +8 -3
  184. package/dist/init/dev-script.d.ts +50 -0
  185. package/dist/init/dev-script.js +66 -0
  186. package/dist/init/format-generated.d.ts +18 -0
  187. package/dist/init/format-generated.js +39 -0
  188. package/dist/init/install-hint.d.ts +9 -0
  189. package/dist/init/install-hint.js +29 -0
  190. package/dist/init/mcp-clients.d.ts +7 -0
  191. package/dist/init/mcp-clients.js +7 -0
  192. package/dist/init/mcp.d.ts +17 -6
  193. package/dist/init/mcp.js +27 -11
  194. package/dist/init/node-io.js +11 -1
  195. package/dist/init/plan-framework.d.ts +0 -5
  196. package/dist/init/plan-framework.js +147 -37
  197. package/dist/init/plan.d.ts +30 -37
  198. package/dist/init/plan.js +50 -60
  199. package/dist/init/preflight.d.ts +28 -0
  200. package/dist/init/preflight.js +38 -0
  201. package/dist/init/refresh-rules.d.ts +39 -0
  202. package/dist/init/refresh-rules.js +51 -0
  203. package/dist/init/run.d.ts +32 -35
  204. package/dist/init/run.js +151 -202
  205. package/dist/init/slash-command.d.ts +1 -1
  206. package/dist/init/snippets.js +90 -31
  207. package/dist/init/vite-config.d.ts +1 -1
  208. package/dist/init/vite-config.js +39 -25
  209. package/dist/init/workspace-apps.d.ts +17 -10
  210. package/dist/init/workspace-apps.js +110 -0
  211. package/dist/input/network-mock-tools.js +3 -6
  212. package/dist/input/real-input.d.ts +2 -0
  213. package/dist/input/real-input.js +28 -7
  214. package/dist/input/scroll-find.d.ts +7 -4
  215. package/dist/input/scroll-find.js +63 -25
  216. package/dist/input/scroll-tools.js +4 -6
  217. package/dist/input/viewport-tools.js +5 -4
  218. package/dist/intent/inline-intent.d.ts +37 -0
  219. package/dist/intent/inline-intent.js +193 -0
  220. package/dist/intent/intent-shard-store.d.ts +72 -0
  221. package/dist/intent/intent-shard-store.js +203 -0
  222. package/dist/intent/intent-shard.d.ts +411 -0
  223. package/dist/intent/intent-shard.js +176 -0
  224. package/dist/intent/intent-store.d.ts +49 -0
  225. package/dist/intent/intent-store.js +133 -0
  226. package/dist/intent/intent-subject.d.ts +45 -0
  227. package/dist/intent/intent-subject.js +177 -0
  228. package/dist/intent/intent-tools.d.ts +2 -0
  229. package/dist/intent/intent-tools.js +143 -0
  230. package/dist/intent/open-intents.d.ts +42 -0
  231. package/dist/intent/open-intents.js +40 -0
  232. package/dist/journal/envelope.d.ts +26 -26
  233. package/dist/journal/journal-recorder.d.ts +23 -0
  234. package/dist/journal/journal-recorder.js +35 -0
  235. package/dist/license/license-env.d.ts +29 -0
  236. package/dist/license/license-env.js +148 -0
  237. package/dist/loopback-agent.d.ts +31 -0
  238. package/dist/loopback-agent.js +57 -0
  239. package/dist/mcp/attach-memory.d.ts +83 -0
  240. package/dist/mcp/attach-memory.js +185 -0
  241. package/dist/mcp/client-identity.d.ts +31 -0
  242. package/dist/mcp/client-identity.js +51 -0
  243. package/dist/mcp/mcp-post-transport.d.ts +26 -0
  244. package/dist/mcp/mcp-post-transport.js +109 -0
  245. package/dist/mcp/mcp-proxy.d.ts +2 -8
  246. package/dist/mcp/mcp-proxy.js +64 -62
  247. package/dist/mcp/mcp.d.ts +2 -0
  248. package/dist/mcp/mcp.js +52 -24
  249. package/dist/mcp/proxy-backoff.d.ts +11 -0
  250. package/dist/mcp/proxy-backoff.js +13 -0
  251. package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
  252. package/dist/mcp/proxy-daemon-probe.js +7 -1
  253. package/dist/mcp/proxy-handshake.d.ts +23 -0
  254. package/dist/mcp/proxy-handshake.js +31 -0
  255. package/dist/mcp/server-instructions.js +45 -3
  256. package/dist/mcp/tool-catalog-cache.d.ts +16 -0
  257. package/dist/mcp/tool-catalog-cache.js +22 -0
  258. package/dist/memory/memory-tools.d.ts +2 -0
  259. package/dist/memory/memory-tools.js +82 -0
  260. package/dist/memory/project-memory.d.ts +34 -0
  261. package/dist/memory/project-memory.js +96 -0
  262. package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
  263. package/dist/oracles/flow-instrument-gaps.js +4 -5
  264. package/dist/oracles/propose-consequences.js +12 -3
  265. package/dist/oracles/self-instrument.d.ts +9 -9
  266. package/dist/oracles/self-instrument.js +19 -13
  267. package/dist/pool/browser-pool.d.ts +75 -5
  268. package/dist/pool/browser-pool.js +144 -5
  269. package/dist/pool/playwright-launcher.js +12 -1
  270. package/dist/project/fs-port.d.ts +3 -0
  271. package/dist/project/fs-port.js +3 -2
  272. package/dist/project/memory-fs.js +6 -1
  273. package/dist/project/project-tools.js +6 -35
  274. package/dist/project/reticle-dir.d.ts +17 -2
  275. package/dist/project/reticle-dir.js +22 -4
  276. package/dist/runs/context-tools.d.ts +2 -0
  277. package/dist/runs/context-tools.js +89 -0
  278. package/dist/runs/run-context.d.ts +40 -0
  279. package/dist/runs/run-context.js +158 -0
  280. package/dist/runs/run-diff.d.ts +2 -1
  281. package/dist/runs/run-store.d.ts +10 -0
  282. package/dist/runs/run-store.js +10 -0
  283. package/dist/runs/verification-sync.d.ts +0 -11
  284. package/dist/runs/verification-sync.js +24 -3
  285. package/dist/runs/verify-server.d.ts +8 -1
  286. package/dist/runs/verify-server.js +14 -2
  287. package/dist/session/command-timeout.d.ts +5 -2
  288. package/dist/session/command-timeout.js +5 -4
  289. package/dist/session/dev-server-probe.d.ts +0 -8
  290. package/dist/session/dev-server-probe.js +1 -1
  291. package/dist/session/last-act.d.ts +7 -1
  292. package/dist/session/last-act.js +2 -2
  293. package/dist/session/lease-visibility.d.ts +67 -0
  294. package/dist/session/lease-visibility.js +76 -0
  295. package/dist/session/live-control-tools.js +1 -42
  296. package/dist/session/no-session-diagnosis.d.ts +33 -0
  297. package/dist/session/no-session-diagnosis.js +82 -24
  298. package/dist/session/no-session-next-action.d.ts +12 -0
  299. package/dist/session/no-session-next-action.js +77 -6
  300. package/dist/session/no-session-watch.d.ts +18 -6
  301. package/dist/session/no-session-watch.js +125 -75
  302. package/dist/session/output-budget.js +4 -1
  303. package/dist/session/pending-commands.d.ts +11 -1
  304. package/dist/session/pending-commands.js +12 -2
  305. package/dist/session/resolve-within.d.ts +34 -0
  306. package/dist/session/resolve-within.js +53 -0
  307. package/dist/session/session-health.d.ts +9 -0
  308. package/dist/session/session-health.js +23 -0
  309. package/dist/session/session-info.d.ts +18 -0
  310. package/dist/session/session-info.js +11 -0
  311. package/dist/session/session-manager.d.ts +14 -0
  312. package/dist/session/session-manager.js +111 -4
  313. package/dist/session/session-mirror.d.ts +16 -0
  314. package/dist/session/session-mirror.js +22 -0
  315. package/dist/session/session-recommendation.d.ts +19 -5
  316. package/dist/session/session-recommendation.js +25 -9
  317. package/dist/session/session-successor.d.ts +37 -0
  318. package/dist/session/session-successor.js +113 -0
  319. package/dist/session/session-tools.js +2 -2
  320. package/dist/session/session.d.ts +53 -3
  321. package/dist/session/session.js +162 -9
  322. package/dist/session/wall-clock.d.ts +12 -0
  323. package/dist/session/wall-clock.js +14 -0
  324. package/dist/setup/agent-configs.d.ts +96 -0
  325. package/dist/setup/agent-configs.js +333 -0
  326. package/dist/setup/agent-io.d.ts +9 -0
  327. package/dist/setup/agent-io.js +16 -0
  328. package/dist/setup/agent-writer.d.ts +51 -0
  329. package/dist/setup/agent-writer.js +115 -0
  330. package/dist/setup/approval-migration.d.ts +47 -0
  331. package/dist/setup/approval-migration.js +68 -0
  332. package/dist/setup/auto-approve.d.ts +85 -0
  333. package/dist/setup/auto-approve.js +150 -0
  334. package/dist/setup/bridge-port.d.ts +15 -0
  335. package/dist/setup/bridge-port.js +24 -0
  336. package/dist/setup/desktop-shape.d.ts +57 -0
  337. package/dist/setup/desktop-shape.js +63 -0
  338. package/dist/setup/dev-server-wait.d.ts +60 -0
  339. package/dist/setup/dev-server-wait.js +65 -0
  340. package/dist/setup/drive-agent.d.ts +71 -0
  341. package/dist/setup/drive-agent.js +171 -0
  342. package/dist/setup/drive-plan.d.ts +68 -0
  343. package/dist/setup/drive-plan.js +130 -0
  344. package/dist/setup/ensure-daemon.d.ts +29 -0
  345. package/dist/setup/ensure-daemon.js +58 -0
  346. package/dist/setup/init-runtime.d.ts +47 -0
  347. package/dist/setup/init-runtime.js +160 -0
  348. package/dist/setup/interrupt.d.ts +25 -0
  349. package/dist/setup/interrupt.js +40 -0
  350. package/dist/setup/license-key.d.ts +39 -0
  351. package/dist/setup/license-key.js +72 -0
  352. package/dist/setup/listeners.d.ts +37 -0
  353. package/dist/setup/listeners.js +85 -0
  354. package/dist/setup/node-effects.d.ts +60 -0
  355. package/dist/setup/node-effects.js +210 -0
  356. package/dist/setup/page-probe.d.ts +39 -0
  357. package/dist/setup/page-probe.js +49 -0
  358. package/dist/setup/relaunch.d.ts +36 -0
  359. package/dist/setup/relaunch.js +57 -0
  360. package/dist/setup/remaining-steps.d.ts +19 -0
  361. package/dist/setup/remaining-steps.js +31 -0
  362. package/dist/setup/run-setup.d.ts +99 -0
  363. package/dist/setup/run-setup.js +187 -0
  364. package/dist/setup/session-pick.d.ts +47 -0
  365. package/dist/setup/session-pick.js +51 -0
  366. package/dist/setup/setup-command.d.ts +45 -0
  367. package/dist/setup/setup-command.js +199 -0
  368. package/dist/setup/setup-options.d.ts +74 -0
  369. package/dist/setup/setup-options.js +67 -0
  370. package/dist/setup/transcripts.d.ts +16 -0
  371. package/dist/setup/transcripts.js +43 -0
  372. package/dist/status-payload.d.ts +14 -1
  373. package/dist/status-payload.js +23 -1
  374. package/dist/telemetry/automation-hint.d.ts +2 -1
  375. package/dist/telemetry/daemon-telemetry.d.ts +8 -1
  376. package/dist/telemetry/daemon-telemetry.js +52 -5
  377. package/dist/telemetry/error-class.js +1 -1
  378. package/dist/telemetry/feedback-context.d.ts +9 -1
  379. package/dist/telemetry/feedback-context.js +60 -29
  380. package/dist/telemetry/feedback-local.d.ts +15 -0
  381. package/dist/telemetry/feedback-local.js +56 -0
  382. package/dist/telemetry/feedback-outbox.d.ts +2 -1
  383. package/dist/telemetry/feedback.d.ts +11 -0
  384. package/dist/telemetry/feedback.js +19 -6
  385. package/dist/telemetry/init-telemetry.d.ts +2 -0
  386. package/dist/telemetry/init-telemetry.js +2 -0
  387. package/dist/telemetry/install-source.d.ts +14 -0
  388. package/dist/telemetry/install-source.js +30 -0
  389. package/dist/telemetry/license-activation.d.ts +7 -1
  390. package/dist/telemetry/license-activation.js +9 -5
  391. package/dist/telemetry/mcp-connection.js +7 -0
  392. package/dist/telemetry/project-profile.d.ts +12 -9
  393. package/dist/telemetry/project-profile.js +12 -7
  394. package/dist/telemetry/proxy-telemetry.d.ts +3 -0
  395. package/dist/telemetry/proxy-telemetry.js +21 -0
  396. package/dist/telemetry/run-telemetry.d.ts +0 -2
  397. package/dist/telemetry/run-telemetry.js +1 -1
  398. package/dist/telemetry/session-metrics.d.ts +12 -0
  399. package/dist/telemetry/session-metrics.js +35 -1
  400. package/dist/telemetry/telemetry.js +17 -2
  401. package/dist/telemetry/tool-refused.d.ts +3 -1
  402. package/dist/telemetry/tool-refused.js +34 -2
  403. package/dist/telemetry/verification-of.js +2 -2
  404. package/dist/tools/act-capsule.d.ts +10 -0
  405. package/dist/tools/act-capsule.js +8 -2
  406. package/dist/tools/act-danger.d.ts +2 -1
  407. package/dist/tools/act-danger.js +11 -3
  408. package/dist/tools/act-observation.d.ts +41 -0
  409. package/dist/tools/act-observation.js +36 -0
  410. package/dist/tools/act-preflight.d.ts +20 -0
  411. package/dist/tools/act-preflight.js +98 -0
  412. package/dist/tools/act-sequence-retry.d.ts +45 -0
  413. package/dist/tools/act-sequence-retry.js +58 -0
  414. package/dist/tools/act-sequence-tool.d.ts +13 -0
  415. package/dist/tools/act-sequence-tool.js +135 -0
  416. package/dist/tools/act-target.d.ts +14 -0
  417. package/dist/tools/act-target.js +47 -0
  418. package/dist/tools/act-tools.d.ts +21 -1
  419. package/dist/tools/act-tools.js +196 -204
  420. package/dist/tools/assert-source.d.ts +22 -0
  421. package/dist/tools/assert-source.js +53 -0
  422. package/dist/tools/assert-verdict.d.ts +20 -3
  423. package/dist/tools/assert-verdict.js +58 -20
  424. package/dist/tools/browser-tools.js +6 -22
  425. package/dist/tools/complete-snapshot.d.ts +48 -0
  426. package/dist/tools/complete-snapshot.js +75 -0
  427. package/dist/tools/coverage-tools.d.ts +10 -1
  428. package/dist/tools/coverage-tools.js +123 -69
  429. package/dist/tools/dynamic-tools.js +53 -13
  430. package/dist/tools/error-recovery.d.ts +25 -14
  431. package/dist/tools/error-recovery.js +78 -9
  432. package/dist/tools/feedback-tools.d.ts +1 -1
  433. package/dist/tools/feedback-tools.js +2 -2
  434. package/dist/tools/invoke-tool.js +121 -11
  435. package/dist/tools/lease-hint.d.ts +10 -0
  436. package/dist/tools/lease-hint.js +53 -4
  437. package/dist/tools/lease-tools.d.ts +35 -5
  438. package/dist/tools/lease-tools.js +191 -13
  439. package/dist/tools/merged-name-redirect.d.ts +13 -0
  440. package/dist/tools/merged-name-redirect.js +22 -0
  441. package/dist/tools/navigate-arrival.d.ts +0 -2
  442. package/dist/tools/navigate-arrival.js +1 -1
  443. package/dist/tools/numeric-bounds.d.ts +100 -0
  444. package/dist/tools/numeric-bounds.js +125 -0
  445. package/dist/tools/observe-tools.js +109 -65
  446. package/dist/tools/read-tools.js +31 -8
  447. package/dist/tools/real-input-attempt.d.ts +32 -0
  448. package/dist/tools/real-input-attempt.js +212 -2
  449. package/dist/tools/reconcile-tools.js +22 -8
  450. package/dist/tools/resolve-target.d.ts +0 -7
  451. package/dist/tools/sdk-marker-probe.d.ts +29 -0
  452. package/dist/tools/sdk-marker-probe.js +57 -0
  453. package/dist/tools/snapshot-delta.d.ts +1 -1
  454. package/dist/tools/tool-kit.d.ts +24 -0
  455. package/dist/tools/tool-kit.js +21 -0
  456. package/dist/tools/tool-names.d.ts +5 -0
  457. package/dist/tools/tool-names.js +5 -0
  458. package/dist/tools/tool-surface.d.ts +85 -6
  459. package/dist/tools/tool-surface.js +122 -2
  460. package/dist/tools/tools-helpers.d.ts +19 -0
  461. package/dist/tools/tools-helpers.js +46 -0
  462. package/dist/tools/tools.d.ts +2 -0
  463. package/dist/tools/tools.js +78 -12
  464. package/dist/tools/unadvertised-help.js +4 -4
  465. package/dist/tools/verify-next-baton.d.ts +53 -0
  466. package/dist/tools/verify-next-baton.js +52 -0
  467. package/dist/update/reticle-deps.d.ts +9 -0
  468. package/dist/update/reticle-deps.js +27 -0
  469. package/dist/update/sdk-sync.d.ts +1 -2
  470. package/dist/update/sdk-sync.js +1 -21
  471. package/dist/update/update-nudge.d.ts +25 -10
  472. package/dist/update/update-nudge.js +32 -3
  473. package/dist/update/updater.d.ts +1 -1
  474. package/dist/version/peer-announce.js +4 -2
  475. package/dist/version/sdk-fix.d.ts +47 -0
  476. package/dist/version/sdk-fix.js +127 -0
  477. package/dist/version/version-nudge.d.ts +14 -0
  478. package/dist/version/version-nudge.js +21 -0
  479. package/dist/version/version-skew.d.ts +38 -3
  480. package/dist/version/version-skew.js +83 -7
  481. package/dist/visual/visual-store.d.ts +6 -6
  482. package/dist/visual/visual-store.js +18 -16
  483. package/dist/visual/visual-tools.js +57 -12
  484. package/docs/README.md +2 -1
  485. package/docs/cli/cloud.mdx +1 -1
  486. package/docs/cli/doctor.mdx +2 -0
  487. package/docs/cli/serve.mdx +4 -2
  488. package/docs/cli/verify.mdx +4 -5
  489. package/docs/cli.mdx +7 -3
  490. package/docs/desktop.mdx +1 -1
  491. package/docs/docs.json +2 -2
  492. package/docs/gates.md +1 -1
  493. package/docs/getting-started.md +19 -3
  494. package/docs/index.mdx +9 -6
  495. package/docs/install-agentic.mdx +38 -12
  496. package/docs/install-manual.mdx +1 -1
  497. package/docs/packages/browser.mdx +2 -2
  498. package/docs/packages/server.mdx +1 -1
  499. package/docs/packages.mdx +8 -6
  500. package/docs/platform-integration.md +17 -0
  501. package/docs/predicates.mdx +8 -0
  502. package/docs/reticle-manual.md +106 -0
  503. package/docs/skill-file.mdx +3 -3
  504. package/docs/telemetry-contract.md +108 -4
  505. package/docs/tools-act-sequence.mdx +16 -0
  506. package/docs/tools-tools-and-run.mdx +2 -4
  507. package/docs/troubleshooting.mdx +9 -0
  508. package/docs/usage.md +4 -3
  509. package/package.json +4 -3
  510. package/dist/init/cursor.d.ts +0 -24
  511. package/dist/init/cursor.js +0 -80
  512. package/dist/session/session-readiness.d.ts +0 -34
  513. package/dist/session/session-readiness.js +0 -36
@@ -8,9 +8,12 @@ export function predicateToExpect(predicate) {
8
8
  case PredicateKind.SIGNAL: {
9
9
  if (predicate.name === undefined)
10
10
  return undefined;
11
- return predicate.dataMatches === undefined
12
- ? { signal: predicate.name }
13
- : { signal: predicate.name, signalData: predicate.dataMatches };
11
+ const signal = { signal: predicate.name };
12
+ if (predicate.dataMatches !== undefined)
13
+ signal.signalData = predicate.dataMatches;
14
+ if (predicate.count !== undefined)
15
+ signal.signalCount = predicate.count;
16
+ return signal;
14
17
  }
15
18
  case PredicateKind.NET: {
16
19
  const net = {};
@@ -75,29 +78,47 @@ export function predicateToExpect(predicate) {
75
78
  }
76
79
  }
77
80
  /**
78
- * The subset a REPLAY actually enforces today.
81
+ * The subset a REPLAY actually enforces.
82
+ *
83
+ * The rule has never changed: never write an assertion into a flow file that nothing evaluates. A
84
+ * flow reporting `grade: "asserted"` while its assertion is read by no one is a false green, in the
85
+ * feature whose entire purpose is preventing them.
79
86
  *
80
- * `flow-replay` checks exactly two things per step: `expect.element.testid` is present after the
81
- * action, and `expect.state` holds. A recorded `net` or `signal` expect is graded as a consequence
82
- * assertion by `classifyFlowAssertions` and then never evaluated — so writing one into a flow file
83
- * would make it report `grade: "asserted"` while nothing checks it.
87
+ * What changed is the SET. This kept only `element.testid` and `state`, because for a long time
88
+ * those were the only kinds replay checked — and the note here said lifting it "requires replay to
89
+ * evaluate them per step". Replay now does exactly that: `assertStepExpect` compiles every
90
+ * remaining kind through `successToPredicate` and waits on it. The condition was met and the filter
91
+ * was not updated, so the two drifted.
84
92
  *
85
- * That is a false green, in the feature whose entire purpose is preventing them, produced by the
86
- * change that was meant to strengthen it. So only the enforced kinds are recorded: a flow that says
87
- * it asserts something must actually go red when that thing breaks.
93
+ * That drift had a cost, and it fell on the agent. `reticle_act_and_wait { until }` IS the agent
94
+ * saying what success means, and `net` is overwhelmingly what it says. Every one of those was
95
+ * discarded at capture, so an agent-recorded flow reached disk assertion-free BY CONSTRUCTION and
96
+ * `reticle_verify` then correctly called it `unverifiable` — the record → save → verify path
97
+ * completing all the way to a run that could never be a pass. Found by driving it.
88
98
  *
89
- * Lifting this is worth doing — net and signal are the assertions agents most often make, and the
90
- * conversion above already handles them — but it requires replay to evaluate them per step (the
91
- * machinery exists: successToPredicate + the predicate engine). Until then, recording them would
92
- * trade a weak flow for a lying one.
99
+ * The list below is exactly what `successToPredicate` reads, plus the testid the step runner
100
+ * asserts directly against the DOM. Anything it cannot compile is still dropped.
93
101
  */
94
102
  export function enforcedOnReplay(expect) {
95
103
  if (expect === undefined)
96
104
  return undefined;
97
105
  const kept = {};
106
+ // The step runner asserts this one directly against the live DOM, before the predicate engine.
98
107
  if (expect.element?.testid !== undefined)
99
108
  kept.element = expect.element;
109
+ // Everything `successToPredicate` can compile. Replay evaluates EVERY kind of expect through it
110
+ // (see assertStepExpect); this list is what that function actually reads.
100
111
  if (expect.state !== undefined)
101
112
  kept.state = expect.state;
113
+ if (expect.signal !== undefined)
114
+ kept.signal = expect.signal;
115
+ if (expect.signalData !== undefined)
116
+ kept.signalData = expect.signalData;
117
+ if (expect.signalCount !== undefined)
118
+ kept.signalCount = expect.signalCount;
119
+ if (expect.net !== undefined)
120
+ kept.net = expect.net;
121
+ if (expect.console !== undefined)
122
+ kept.console = expect.console;
102
123
  return 0 === Object.keys(kept).length ? undefined : kept;
103
124
  }
@@ -13,12 +13,15 @@ export interface RecordedStep {
13
13
  interface ActiveRecording {
14
14
  cursor: number;
15
15
  steps: RecordedStep[];
16
+ /** The route the journey began on. See CompiledProgram.startPath. */
17
+ startPath?: string;
16
18
  }
17
19
  /** A finished, replayable program compiled from a recording. */
18
20
  export interface CompiledProgram {
19
21
  name: string;
20
22
  version: number;
21
23
  steps: RecordedStep[];
24
+ startPath?: string;
22
25
  }
23
26
  /**
24
27
  * Tracks in-flight recordings (name -> { buffer cursor at record_start, captured steps })
@@ -26,7 +29,7 @@ export interface CompiledProgram {
26
29
  */
27
30
  export declare class RecordingStore {
28
31
  #private;
29
- start(name: string, cursor: number): void;
32
+ start(name: string, cursor: number, startPath?: string): void;
30
33
  isRecording(name: string): boolean;
31
34
  /**
32
35
  * Number of steps captured so far in the named ACTIVE recording (0 if it
@@ -41,5 +44,14 @@ export declare class RecordingStore {
41
44
  saveCompiled(program: CompiledProgram): void;
42
45
  getCompiled(name: string): CompiledProgram | undefined;
43
46
  active(): string[];
47
+ /**
48
+ * Names of recordings that have been STOPPED and compiled.
49
+ *
50
+ * The mirror of `active()`, and needed for the same defaulting: after `reticle_record stop` a
51
+ * recording is no longer active, so resolving "the obvious one" from `active()` finds nothing.
52
+ * Saving it then demanded the exact name back from the caller, which is a thing to remember for
53
+ * no reason when exactly one recording exists.
54
+ */
55
+ compiled(): string[];
44
56
  }
45
57
  export {};
@@ -5,8 +5,12 @@
5
5
  export class RecordingStore {
6
6
  #active = new Map();
7
7
  #compiled = new Map();
8
- start(name, cursor) {
9
- this.#active.set(name, { cursor, steps: [] });
8
+ start(name, cursor, startPath) {
9
+ this.#active.set(name, {
10
+ cursor,
11
+ steps: [],
12
+ ...(startPath === undefined ? {} : { startPath }),
13
+ });
10
14
  }
11
15
  isRecording(name) {
12
16
  return this.#active.has(name);
@@ -39,4 +43,15 @@ export class RecordingStore {
39
43
  active() {
40
44
  return [...this.#active.keys()];
41
45
  }
46
+ /**
47
+ * Names of recordings that have been STOPPED and compiled.
48
+ *
49
+ * The mirror of `active()`, and needed for the same defaulting: after `reticle_record stop` a
50
+ * recording is no longer active, so resolving "the obvious one" from `active()` finds nothing.
51
+ * Saving it then demanded the exact name back from the caller, which is a thing to remember for
52
+ * no reason when exactly one recording exists.
53
+ */
54
+ compiled() {
55
+ return [...this.#compiled.keys()];
56
+ }
42
57
  }
@@ -13,8 +13,28 @@ export declare function ambiguousTestidNote(value: string): string;
13
13
  * resolution — can't drift between them.
14
14
  */
15
15
  export declare function queryRefs(result: CommandResult): string[];
16
- /** A destructive-action confirmation is one-shot and must never persist into a recording. */
17
- export declare function replayActionArgs(value: unknown, confirmDangerous?: boolean): Record<string, unknown>;
16
+ /**
17
+ * The environment variable that supplies one redacted field.
18
+ *
19
+ * Named after the field so it is guessable from the flow alone: `auth-password` is read from
20
+ * `RETICLE_SECRET_AUTH_PASSWORD`. A scheme requiring a lookup table would mean the flow says a
21
+ * value is missing and cannot say what to set.
22
+ */
23
+ export declare const secretEnvKey: (field: string) => string;
24
+ /**
25
+ * A destructive-action confirmation is one-shot and must never persist into a recording.
26
+ *
27
+ * This is also where a REDACTED fill is supplied back. Stripping credentials out of a git-checked
28
+ * flow is only half a fix: sign-in still has to replay, and a flow that drifts at step two forever
29
+ * because its own password was removed is a flow nobody keeps — along with everything behind the
30
+ * login it guards. The value comes from the environment, the one place a secret can live that is
31
+ * neither the customer's repository nor our database.
32
+ *
33
+ * Left as the PLACEHOLDER when nothing supplies it, rather than blanked. The replay then fails at
34
+ * the login form with `<redacted: supply at replay>` visible in the field, which names its own fix;
35
+ * an empty box fails identically and tells the reader nothing.
36
+ */
37
+ export declare function replayActionArgs(value: unknown, confirmDangerous?: boolean, field?: string): Record<string, unknown>;
18
38
  /**
19
39
  * Capture an act into every in-flight recording, or do nothing when none is running.
20
40
  *
@@ -1,3 +1,4 @@
1
+ import { REDACTED_FILL } from './flows.js';
1
2
  import { DANGEROUS_ACTION_CONFIRM_ARG, ReticleCommand, QueryBy, } from '@reticlehq/core';
2
3
  import { ReticleTool } from '../tools/tool-names.js';
3
4
  import { predicateToExpect, enforcedOnReplay } from './predicate-to-expect.js';
@@ -23,12 +24,37 @@ export function queryRefs(result) {
23
24
  const elements = Array.isArray(payload['elements']) ? payload['elements'] : [];
24
25
  return elements.map((e) => asString(asRecord(e)['ref']) ?? '').filter((r) => r.length > 0);
25
26
  }
26
- /** A destructive-action confirmation is one-shot and must never persist into a recording. */
27
- export function replayActionArgs(value, confirmDangerous = false) {
27
+ /**
28
+ * The environment variable that supplies one redacted field.
29
+ *
30
+ * Named after the field so it is guessable from the flow alone: `auth-password` is read from
31
+ * `RETICLE_SECRET_AUTH_PASSWORD`. A scheme requiring a lookup table would mean the flow says a
32
+ * value is missing and cannot say what to set.
33
+ */
34
+ export const secretEnvKey = (field) => `RETICLE_SECRET_${field.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase()}`;
35
+ /**
36
+ * A destructive-action confirmation is one-shot and must never persist into a recording.
37
+ *
38
+ * This is also where a REDACTED fill is supplied back. Stripping credentials out of a git-checked
39
+ * flow is only half a fix: sign-in still has to replay, and a flow that drifts at step two forever
40
+ * because its own password was removed is a flow nobody keeps — along with everything behind the
41
+ * login it guards. The value comes from the environment, the one place a secret can live that is
42
+ * neither the customer's repository nor our database.
43
+ *
44
+ * Left as the PLACEHOLDER when nothing supplies it, rather than blanked. The replay then fails at
45
+ * the login form with `<redacted: supply at replay>` visible in the field, which names its own fix;
46
+ * an empty box fails identically and tells the reader nothing.
47
+ */
48
+ export function replayActionArgs(value, confirmDangerous = false, field) {
28
49
  const args = { ...asRecord(value) };
29
50
  delete args[DANGEROUS_ACTION_CONFIRM_ARG];
30
51
  if (confirmDangerous)
31
52
  args[DANGEROUS_ACTION_CONFIRM_ARG] = true;
53
+ if (REDACTED_FILL === args['value'] && field !== undefined) {
54
+ const supplied = process.env[secretEnvKey(field)];
55
+ if (supplied !== undefined && supplied.length > 0)
56
+ args['value'] = supplied;
57
+ }
32
58
  return args;
33
59
  }
34
60
  /** The element's source location from an action result, when the framework stamped one. */
@@ -1,5 +1,6 @@
1
1
  import { VerifyMode } from '../cloud/cloud-config.js';
2
- import { submitServerVerification } from '../cloud/cloud-sync.js';
2
+ import { CLOUD_VERIFY_TIMEOUT_MS, cloudFetch, submitServerVerification, } from '../cloud/cloud-sync.js';
3
+ import { log } from '../log.js';
3
4
  const PASS = 'pass';
4
5
  const UNVERIFIED = 'unverified';
5
6
  const SOURCE = 'reticle-mcp';
@@ -31,7 +32,20 @@ export async function runServerVerify(deps, cloud, sessionId, flows) {
31
32
  // The server hits the URL itself; with no URL (or a localhost one it can't reach) fall back to local.
32
33
  if (previewUrl === undefined || 0 === previewUrl.length)
33
34
  return null;
34
- const report = await submitServerVerification({ previewUrl, flows, source: SOURCE }, cloud.config, (url, init) => fetch(url, init));
35
+ // The submit blocks while the hosted runner drives a real browser, so it gets the long budget — but a
36
+ // budget it must have: this is awaited inside the verify tool, and an unbounded wait here is an MCP
37
+ // call that never returns. A timeout is reported and then falls back to the local replay below.
38
+ const report = await submitServerVerification({ previewUrl, flows, source: SOURCE }, cloud.config, async (url, init) => {
39
+ try {
40
+ return await cloudFetch(url, init, CLOUD_VERIFY_TIMEOUT_MS);
41
+ }
42
+ catch (error) {
43
+ log('cloud-server-verify-failed', {
44
+ error: error instanceof Error ? error.message : String(error),
45
+ });
46
+ throw error;
47
+ }
48
+ });
35
49
  if (null === report)
36
50
  return null;
37
51
  // The hosted runner said it couldn't actually verify (e.g. it's not enabled yet). Never surface that as
@@ -5,6 +5,7 @@ import { attributedFailures } from './attributed-failure.js';
5
5
  import { Verified } from '@reticlehq/core';
6
6
  import { ReticleTool } from '../tools/tool-names.js';
7
7
  import { asNumber, asRecord, asString } from '../tools/tools-helpers.js';
8
+ import { workerCountSchema } from '../tools/numeric-bounds.js';
8
9
  import { loadNamedFlows, resolveChangedFiles } from '../cli/cli-flow-commands.js';
9
10
  import { affectedSavedFlows } from './flow-sources.js';
10
11
  import { FLOW_TOOLS } from './flow-tools.js';
@@ -25,8 +26,7 @@ export const VERIFY_CHANGE_TOOLS = [
25
26
  .string()
26
27
  .optional()
27
28
  .describe('Git ref to diff against (e.g. "HEAD~1", "main"). Unioned with `files`.'),
28
- parallel: z
29
- .number()
29
+ parallel: workerCountSchema
30
30
  .optional()
31
31
  .describe('Replay this many affected flows at once (needs the browser pool).'),
32
32
  sessionId: z
@@ -197,7 +197,13 @@ async function inspectAfterReplay(deps, args, cursor) {
197
197
  }
198
198
  try {
199
199
  const session = deps.sessions.resolve(asString(args['sessionId']));
200
- const contradictions = findContradictions(session.eventsSince(cursor));
200
+ const contradictions = findContradictions(session.eventsSince(cursor), {
201
+ currentDocumentId: session.currentDocumentId,
202
+ currentEditEpoch: session.currentEditEpoch,
203
+ appOrigin: session.url,
204
+ // The replay is the action, and the cursor is where it started.
205
+ actionSince: cursor,
206
+ });
201
207
  const snapshot = await session.command(ReticleCommand.SNAPSHOT, {
202
208
  mode: SnapshotMode.INTERACTIVE,
203
209
  });
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export { BlindSpotKind } from '@reticlehq/core';
8
8
  import { BlindSpotKind, type ReticleEvent } from '@reticlehq/core';
9
- interface BlindSpot {
9
+ export interface BlindSpot {
10
10
  kind: BlindSpotKind;
11
11
  count: number;
12
12
  }
@@ -55,6 +55,15 @@ export declare function buildCoverageStatement(spots: readonly BlindSpot[]): Cov
55
55
  * from what the SDK observed during the action, with no extra round-trip.
56
56
  */
57
57
  export declare function blindSpotsFromEvents(events: readonly ReticleEvent[]): BlindSpot[];
58
+ /**
59
+ * Drop Electron-only coverage rows unless this session is actually an Electron renderer.
60
+ *
61
+ * The kinds live in the same vocabulary as "no store registered", so listing whatever is present
62
+ * reported a Vite + React tab as an un-instrumented Electron renderer. The session already knows
63
+ * the runtime (PAGE_HEALTH). Unknown runtime (older SDK) keeps the rows — dropping them would hide
64
+ * a real missing-preload warning.
65
+ */
66
+ export declare function spotsForRuntime(spots: readonly BlindSpot[], runtime: string | undefined): BlindSpot[];
58
67
  /**
59
68
  * Blind spots from the session's remembered LEVEL state rather than from a window of events.
60
69
  *
@@ -63,25 +72,35 @@ export declare function blindSpotsFromEvents(events: readonly ReticleEvent[]): B
63
72
  * reports "full" for a page a third of which is unobservable — and the act tool's own description
64
73
  * tells harnesses to gate on that block. Ask the session what is true now instead of inferring it
65
74
  * from what happened to be said recently.
75
+ *
76
+ * `runtime` gates Electron-only kinds (see `spotsForRuntime`) so a web session cannot inherit them
77
+ * from the coverage vocabulary.
66
78
  */
67
- export declare function blindSpotsFromState(state: Readonly<Record<string, number>>): BlindSpot[];
79
+ export declare function blindSpotsFromState(state: Readonly<Record<string, number>>, runtime?: string): BlindSpot[];
68
80
  /**
69
- * Events the BROWSER's own transport queue threw away inside this window, or 0.
81
+ * The narrow slice of a predicate the absence-blind-spot check needs.
70
82
  *
71
- * `RATE_LIMITED` — the BRIDGE sampling because events arrived faster than its per-second cap — is
72
- * already the one blind spot that `impeachesCapture`, on the reasoning that a green over a window
73
- * you did not fully see "would only describe what was observed". `TRANSPORT_OVERFLOW` is the exact
74
- * same loss on the other side of the wire, and it was read in journal rollups and NOWHERE on the
75
- * verdict path. So the identical condition downgraded a verdict from one side of the socket and was
76
- * invisible from the other: a window that dropped 34 events graded `proved`, in a sentence whose own
77
- * words were "over a clean capture".
83
+ * Kept minimal to avoid pulling the full Predicate type (and its transitive deps) into the
84
+ * honesty module. Anything that quacks like this works.
85
+ */
86
+ interface AbsencePredicate {
87
+ kind: string;
88
+ absent?: boolean;
89
+ query?: {
90
+ scope?: unknown;
91
+ };
92
+ }
93
+ /**
94
+ * When an absence assertion targets a page with regions Reticle cannot observe, "the element is
95
+ * absent" means only "it is absent in what I CAN see". Returns a note when that distinction
96
+ * matters, undefined otherwise.
78
97
  *
79
- * `TRUNCATED` is deliberately NOT counted. It names the channel it capped (a DOM-mutation flood on
80
- * any busy page) and is routine churn; treating it as lost evidence would caveat every verdict on
81
- * every real app, and a caveat that is always present is one nobody reads. TRANSPORT_OVERFLOW is the
82
- * honest "arbitrary events are gone" marker — it cannot say which.
98
+ * Fires on three blind-spot kinds:
99
+ * - CROSS_ORIGIN_IFRAME (only when the predicate is scoped to a frame region)
100
+ * - VIRTUALIZED_UNMOUNTED — a row that was never rendered could hold the element
101
+ * - CLOSED_SHADOW_ROOT — a subtree Reticle cannot see into
83
102
  */
84
- export declare function droppedByTransport(events: readonly ReticleEvent[]): number;
103
+ export declare function absenceBlindSpotNote(predicate: AbsencePredicate, spots: readonly BlindSpot[]): string | undefined;
85
104
  /** The impeaching note for a transport gap, or undefined when the window was intact. */
86
105
  export declare function transportGapNote(events: readonly ReticleEvent[]): string | undefined;
87
106
  /**
@@ -93,3 +112,20 @@ export declare function transportGapNote(events: readonly ReticleEvent[]): strin
93
112
  * predicate would eventually disagree with the first about what "unwatched" means.
94
113
  */
95
114
  export declare function isStateUnwatched(spots: readonly BlindSpot[]): boolean;
115
+ /**
116
+ * Does a passing verdict on this predicate REST on the window being complete?
117
+ *
118
+ * Only an absence claim does. A positive assertion that passed found its evidence, and events aged
119
+ * out elsewhere in the buffer do not unmake it. An absence claim concluded "nothing is there" — and
120
+ * the thing the buffer dropped is precisely the disproof, so "absent" degrades to "absent in what I
121
+ * still hold".
122
+ *
123
+ * The distinction is not optional. Scarce-loss is recorded for AGE eviction too, so `lostSince(0)`
124
+ * is true on any session older than the 60s cutoff; impeaching every verdict over a caller-chosen
125
+ * window would make `unknown` the answer to everything, which this repo has already paid for once.
126
+ */
127
+ export declare function restsOnCompleteWindow(predicate: {
128
+ kind: string;
129
+ absent?: boolean;
130
+ predicate?: unknown;
131
+ }): boolean;
@@ -7,7 +7,7 @@
7
7
  // The kind enum lives in core (it crosses the wire in a BLIND_SPOT event); re-exported here so existing
8
8
  // honesty-side imports keep working.
9
9
  export { BlindSpotKind } from '@reticlehq/core';
10
- import { BlindSpotKind, EventType, ReticleEnv, TRANSPORT_LIMITS, } from '@reticlehq/core';
10
+ import { AppRuntime, BlindSpotKind, EventType, PredicateKind, ReticleEnv, TRANSPORT_LIMITS, isDesktopBlindSpot, } from '@reticlehq/core';
11
11
  /**
12
12
  * Whether the observation window saw everything it was asked about.
13
13
  *
@@ -107,6 +107,19 @@ export function blindSpotsFromEvents(events) {
107
107
  }
108
108
  return [...latest].map(([kind, count]) => ({ kind, count }));
109
109
  }
110
+ /**
111
+ * Drop Electron-only coverage rows unless this session is actually an Electron renderer.
112
+ *
113
+ * The kinds live in the same vocabulary as "no store registered", so listing whatever is present
114
+ * reported a Vite + React tab as an un-instrumented Electron renderer. The session already knows
115
+ * the runtime (PAGE_HEALTH). Unknown runtime (older SDK) keeps the rows — dropping them would hide
116
+ * a real missing-preload warning.
117
+ */
118
+ export function spotsForRuntime(spots, runtime) {
119
+ if (runtime === undefined || AppRuntime.ELECTRON === runtime)
120
+ return [...spots];
121
+ return spots.filter((spot) => !isDesktopBlindSpot(spot.kind));
122
+ }
110
123
  /**
111
124
  * Blind spots from the session's remembered LEVEL state rather than from a window of events.
112
125
  *
@@ -115,9 +128,16 @@ export function blindSpotsFromEvents(events) {
115
128
  * reports "full" for a page a third of which is unobservable — and the act tool's own description
116
129
  * tells harnesses to gate on that block. Ask the session what is true now instead of inferring it
117
130
  * from what happened to be said recently.
131
+ *
132
+ * `runtime` gates Electron-only kinds (see `spotsForRuntime`) so a web session cannot inherit them
133
+ * from the coverage vocabulary.
118
134
  */
119
- export function blindSpotsFromState(state) {
120
- return Object.entries(state).map(([kind, count]) => ({ kind: kind, count }));
135
+ export function blindSpotsFromState(state, runtime) {
136
+ const spots = Object.entries(state).map(([kind, count]) => ({
137
+ kind: kind,
138
+ count,
139
+ }));
140
+ return spotsForRuntime(spots, runtime);
121
141
  }
122
142
  /**
123
143
  * Events the BROWSER's own transport queue threw away inside this window, or 0.
@@ -135,7 +155,7 @@ export function blindSpotsFromState(state) {
135
155
  * every real app, and a caveat that is always present is one nobody reads. TRANSPORT_OVERFLOW is the
136
156
  * honest "arbitrary events are gone" marker — it cannot say which.
137
157
  */
138
- export function droppedByTransport(events) {
158
+ function droppedByTransport(events) {
139
159
  let dropped = 0;
140
160
  for (const e of events) {
141
161
  if (e.type !== EventType.TRANSPORT_OVERFLOW)
@@ -145,6 +165,29 @@ export function droppedByTransport(events) {
145
165
  }
146
166
  return dropped;
147
167
  }
168
+ /**
169
+ * When an absence assertion targets a page with regions Reticle cannot observe, "the element is
170
+ * absent" means only "it is absent in what I CAN see". Returns a note when that distinction
171
+ * matters, undefined otherwise.
172
+ *
173
+ * Fires on three blind-spot kinds:
174
+ * - CROSS_ORIGIN_IFRAME (only when the predicate is scoped to a frame region)
175
+ * - VIRTUALIZED_UNMOUNTED — a row that was never rendered could hold the element
176
+ * - CLOSED_SHADOW_ROOT — a subtree Reticle cannot see into
177
+ */
178
+ export function absenceBlindSpotNote(predicate, spots) {
179
+ if ('element' !== predicate.kind || true !== predicate.absent)
180
+ return undefined;
181
+ const relevant = spots.filter((spot) => spot.count > 0 &&
182
+ (spot.kind === BlindSpotKind.CROSS_ORIGIN_IFRAME
183
+ ? undefined !== predicate.query?.scope
184
+ : spot.kind === BlindSpotKind.VIRTUALIZED_UNMOUNTED ||
185
+ spot.kind === BlindSpotKind.CLOSED_SHADOW_ROOT));
186
+ if (0 === relevant.length)
187
+ return undefined;
188
+ const statement = buildCoverageStatement(relevant);
189
+ return `the absence assertion targeted a region Reticle could not observe (${statement.note ?? 'partial coverage'}), so a passing DOM check cannot prove absence`;
190
+ }
148
191
  /** The impeaching note for a transport gap, or undefined when the window was intact. */
149
192
  export function transportGapNote(events) {
150
193
  const dropped = droppedByTransport(events);
@@ -163,3 +206,20 @@ export function transportGapNote(events) {
163
206
  export function isStateUnwatched(spots) {
164
207
  return spots.some((spot) => spot.kind === BlindSpotKind.UNWATCHED_STATE && spot.count > 0);
165
208
  }
209
+ /**
210
+ * Does a passing verdict on this predicate REST on the window being complete?
211
+ *
212
+ * Only an absence claim does. A positive assertion that passed found its evidence, and events aged
213
+ * out elsewhere in the buffer do not unmake it. An absence claim concluded "nothing is there" — and
214
+ * the thing the buffer dropped is precisely the disproof, so "absent" degrades to "absent in what I
215
+ * still hold".
216
+ *
217
+ * The distinction is not optional. Scarce-loss is recorded for AGE eviction too, so `lostSince(0)`
218
+ * is true on any session older than the 60s cutoff; impeaching every verdict over a caller-chosen
219
+ * window would make `unknown` the answer to everything, which this repo has already paid for once.
220
+ */
221
+ export function restsOnCompleteWindow(predicate) {
222
+ if (PredicateKind.NOT === predicate.kind)
223
+ return true;
224
+ return true === predicate.absent;
225
+ }
@@ -0,0 +1,150 @@
1
+ import { type Intent, type JournalAction } from '@reticlehq/core';
2
+ /**
3
+ * Were `reticle_context` and `reticle_intent` used at all — and did using them change anything?
4
+ *
5
+ * Both shipped with the same pre-registered disproof: *if agents never call it, cut it*. Nothing
6
+ * recorded whether they were called, so the disproof could never be run, and a feature whose
7
+ * disproof cannot be run is one nobody can kill. This is the smallest thing that makes the question
8
+ * answerable, per session, locally — an instrument, not a scold. The numbers exist so a FEATURE can
9
+ * be removed, never so an agent can be blamed for how it drove.
10
+ *
11
+ * ## Fold first, record only what nothing records
12
+ *
13
+ * Three of the four questions are already answerable from what is on disk. The intent ledger says
14
+ * what was declared; the journal says what was driven and what verdicts were drawn. Those are folded
15
+ * here and never re-stored, for the reason `run-context.ts` gives at length: two ways to compute one
16
+ * number is worse than one way.
17
+ *
18
+ * The journal does NOT record read-only calls — it holds actions and verdicts only. So "was
19
+ * `reticle_context` called" has no ledger to fold, and that one thing is recorded, in memory, on the
20
+ * session, by `CaptureLedger`. It is the whole of the new state.
21
+ *
22
+ * ## Absent is not zero
23
+ *
24
+ * A session this daemon has recorded nothing for — journaling off, a session adopted after the fact
25
+ * — reports `observed: false` and states no counts at all. It has not established that the features
26
+ * went unused; it has established that it was not watching. This repo has shipped the other bug
27
+ * twice.
28
+ */
29
+ /**
30
+ * The calls this instrument needs and the journal does not keep.
31
+ *
32
+ * Deliberately a short explicit list rather than "everything that is not an action". Under-inclusion
33
+ * is the safe direction: a read tool missing from here lowers the hit side and the miss side by the
34
+ * same call, so the comparison the disproof turns on stays fair. Over-inclusion is not — a lifecycle
35
+ * or disk-side tool landing in here would be counted as a "read" the agent made of the app, which it
36
+ * was not.
37
+ */
38
+ export declare const CAPTURED_TOOLS: ReadonlySet<string>;
39
+ /** One recorded call: which tool, what it was about, and how far into the run it happened. */
40
+ export interface CapturedCall {
41
+ tool: string;
42
+ /** The ref or target the call named, when it named one. Absent for a whole-page read. */
43
+ subject?: string;
44
+ /**
45
+ * The session's dispatched-action count at the moment of the call — the same counter that mints
46
+ * journal action ids, so it lines up with `reticle_context`'s own `step` whenever the journal is on.
47
+ */
48
+ afterActions: number;
49
+ }
50
+ /**
51
+ * The one thing that is stored rather than folded: the read-only calls the journal never sees.
52
+ *
53
+ * In memory and per session, the same shape as `GapLedger`. It is not durable on purpose — the
54
+ * question is about one run, and a second file on disk would be a second thing to reconcile with the
55
+ * journal.
56
+ */
57
+ export declare class CaptureLedger {
58
+ #private;
59
+ /**
60
+ * One call, under the name of the tool that RAN — every tool, not just the read set above.
61
+ *
62
+ * Separate from `note` because it answers a different question and is bounded differently: this
63
+ * map cannot outgrow the tool table however long the session runs, so it needs no cap and never
64
+ * has to report a floor. See honesty/tool-hit-rate.ts for what it feeds.
65
+ */
66
+ noteTool(tool: string): void;
67
+ /** How many times each tool was called this session, in first-call order. */
68
+ toolCalls(): ReadonlyMap<string, number>;
69
+ note(call: CapturedCall): void;
70
+ calls(): readonly CapturedCall[];
71
+ /** How many calls fell off the front. Non-zero means the counts below are a floor. */
72
+ get dropped(): number;
73
+ }
74
+ /**
75
+ * Record one captured call against the session that made it.
76
+ *
77
+ * Tolerates a session that carries no ledger, exactly as `noteSessionGaps` does and for the same
78
+ * reason: `Session` is satisfied structurally, and many specs — plus any consumer embedding this
79
+ * engine — build one as an object literal. Such a session is a real caller that simply keeps no
80
+ * ledger, and an instrument must never be the reason a tool call throws. Its calls are then absent
81
+ * rather than counted as zero, which is what `observed: false` exists to say.
82
+ */
83
+ export declare function noteCapturedCall(session: {
84
+ capture?: CaptureLedger;
85
+ actionCount?: number;
86
+ }, call: Omit<CapturedCall, 'afterActions'>): void;
87
+ /**
88
+ * Count one call against the session that made it, whichever tool it was.
89
+ *
90
+ * Tolerant of a ledger-less session for exactly the reason `noteCapturedCall` is: its calls are then
91
+ * absent rather than counted as zero, which is what `observed: false` exists to say.
92
+ */
93
+ export declare function noteToolDispatched(session: {
94
+ capture?: CaptureLedger;
95
+ }, tool: string): void;
96
+ /** What one session's use of the two features looks like. Everything but `observed` is omitted when nothing was recorded. */
97
+ export interface FeatureCapture {
98
+ /** False when this daemon recorded nothing for the session — "not watched", never "not used". */
99
+ observed: boolean;
100
+ /** The ledger dropped calls, so every count is a floor. Present only when true. */
101
+ truncated?: boolean;
102
+ context?: {
103
+ calls: number;
104
+ /** The dispatched-action count each call was made at, in order. */
105
+ atSteps: number[];
106
+ acted: number;
107
+ refetched: number;
108
+ readOther: number;
109
+ nothingAfter: number;
110
+ };
111
+ intents?: {
112
+ declared: number;
113
+ open: number;
114
+ };
115
+ missed?: {
116
+ verdictsWithNoIntentDeclared: number;
117
+ refetchedEstablished: number;
118
+ };
119
+ }
120
+ /**
121
+ * Did calling `reticle_context` change what happened next?
122
+ *
123
+ * The mechanical proxy, stated so it can be argued with: look at the very next recorded call. If an
124
+ * action was dispatched before it — the action counter moved — the agent ACTED, and the context
125
+ * plausibly helped. If instead the next call is a read of a subject the context had just supplied,
126
+ * the agent re-fetched what it had been handed, and the context did not help.
127
+ *
128
+ * ## What this proxy CANNOT see
129
+ *
130
+ * - Whether the agent READ the response, reasoned over it, or wrote a better answer from it. There
131
+ * is no signal for that anywhere, and this must not be mistaken for one.
132
+ * - Anything that happens between two Reticle calls: source edits, calls to other MCP servers, a
133
+ * turn ending, a human interrupting. A long gap is invisible and reads as adjacency.
134
+ * - Reads that name no ref or target (a whole-page snapshot). They land in `readOther`, never in
135
+ * `refetched`, so a snapshot that duplicated the context is under-counted, not over-counted.
136
+ * - Causation. `acted` is proximity in a call sequence, nothing more. An agent that would have acted
137
+ * anyway is indistinguishable from one the context unblocked.
138
+ * - `reticle_context` called with no session connected — it answers there by design, and there is no
139
+ * per-session ledger to record it against, so those calls are missing entirely.
140
+ * - Two agents driving one session. Their calls interleave into one sequence and "next call" stops
141
+ * meaning "next thing that agent did".
142
+ */
143
+ export declare function foldFeatureCapture(input: {
144
+ calls: readonly CapturedCall[];
145
+ dropped: number;
146
+ actions: readonly JournalAction[];
147
+ intents: readonly Intent[];
148
+ /** The session's dispatched-action count now, so a run that ended on an action is not read as idle. */
149
+ finalActions: number;
150
+ }): FeatureCapture;