@reticlehq/server 2.13.1 → 2.14.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 (449) hide show
  1. package/SKILL.md +8 -18
  2. package/bin/reticle.js +3 -0
  3. package/dist/bridge/auth-failure-reason.js +12 -1
  4. package/dist/bridge/bridge-security.d.ts +8 -0
  5. package/dist/bridge/bridge-security.js +6 -1
  6. package/dist/bridge/bridge.d.ts +3 -0
  7. package/dist/bridge/bridge.js +23 -1
  8. package/dist/bridge/bridge.test-harness.d.ts +10 -1
  9. package/dist/bridge/bridge.test-harness.js +7 -1
  10. package/dist/bridge/pairing-token.d.ts +17 -0
  11. package/dist/bridge/pairing-token.js +26 -0
  12. package/dist/bridge/token-auth.d.ts +7 -0
  13. package/dist/bridge/token-auth.js +7 -1
  14. package/dist/capsule/capsule-store.d.ts +4 -4
  15. package/dist/capsule/capsule.d.ts +1 -1
  16. package/dist/capsule/capsule.js +3 -3
  17. package/dist/capsule/causal-summary.d.ts +21 -1
  18. package/dist/capsule/causal-summary.js +8 -6
  19. package/dist/capsule/divergence.d.ts +1 -1
  20. package/dist/capsule/divergence.js +26 -10
  21. package/dist/chromium-launch-options.d.ts +2 -1
  22. package/dist/cli/adhoc-verdict.d.ts +58 -0
  23. package/dist/cli/adhoc-verdict.js +115 -0
  24. package/dist/cli/chromium-hint.d.ts +18 -4
  25. package/dist/cli/chromium-hint.js +54 -6
  26. package/dist/cli/cli-doctor.js +1 -2
  27. package/dist/cli/cli-flow-commands.d.ts +16 -2
  28. package/dist/cli/cli-flow-commands.js +28 -9
  29. package/dist/cli/cli-parse-feedback.js +1 -1
  30. package/dist/cli/cli-parse.d.ts +2 -1
  31. package/dist/cli/cli-parse.js +38 -0
  32. package/dist/cli/cli-port.d.ts +5 -13
  33. package/dist/cli/cli-port.js +5 -38
  34. package/dist/cli/cli-update-commands.js +4 -4
  35. package/dist/cli/cli-verify.d.ts +24 -7
  36. package/dist/cli/cli-verify.js +87 -13
  37. package/dist/cli/cloud-keystore.d.ts +2 -1
  38. package/dist/cli/cloud-login.d.ts +2 -1
  39. package/dist/cli/cloud-session.d.ts +2 -1
  40. package/dist/cli/cloud-session.js +1 -1
  41. package/dist/cli/daemon-start-options.d.ts +2 -1
  42. package/dist/cli/device-flow.d.ts +2 -2
  43. package/dist/cli/doctor-daemon-line.d.ts +2 -1
  44. package/dist/cli/doctor-project-line.d.ts +2 -1
  45. package/dist/cli/doctor-rows.d.ts +2 -1
  46. package/dist/cli/drive-attach.d.ts +4 -3
  47. package/dist/cli/gate-hook-message.d.ts +2 -1
  48. package/dist/cli/status-next-action.js +10 -11
  49. package/dist/cli.d.ts +1 -0
  50. package/dist/cli.js +21 -9
  51. package/dist/cloud/account-state.d.ts +20 -0
  52. package/dist/cloud/account-state.js +65 -0
  53. package/dist/cloud/cloud-sync.d.ts +46 -3
  54. package/dist/cloud/cloud-sync.js +116 -0
  55. package/dist/cloud/sync-cycle.d.ts +5 -4
  56. package/dist/cloud/sync-cycle.js +1 -1
  57. package/dist/cloud/sync-daemon.d.ts +3 -19
  58. package/dist/cloud/sync-daemon.js +2 -2
  59. package/dist/crawl/crawl-tools.js +1 -1
  60. package/dist/crawl/crawl.d.ts +5 -5
  61. package/dist/crawl/crawl.js +138 -4
  62. package/dist/daemon/daemon-resilience.d.ts +3 -2
  63. package/dist/daemon/daemon-resilience.js +43 -4
  64. package/dist/daemon/daemon-resolve.d.ts +0 -9
  65. package/dist/daemon/daemon-resolve.js +2 -2
  66. package/dist/daemon/dev-servers.d.ts +1 -1
  67. package/dist/daemon/dev-servers.js +1 -1
  68. package/dist/daemon/loopback-alias.d.ts +2 -1
  69. package/dist/daemon/startup-failure.d.ts +11 -0
  70. package/dist/daemon/startup-failure.js +46 -0
  71. package/dist/events/contradictions.d.ts +10 -16
  72. package/dist/events/contradictions.js +149 -17
  73. package/dist/events/declared.d.ts +13 -1
  74. package/dist/events/declared.js +59 -3
  75. package/dist/events/echo-mismatch.js +75 -6
  76. package/dist/events/lineage.d.ts +52 -0
  77. package/dist/events/lineage.js +122 -0
  78. package/dist/events/net-evidence.d.ts +21 -0
  79. package/dist/events/net-evidence.js +81 -0
  80. package/dist/events/predicate-console.d.ts +13 -0
  81. package/dist/events/predicate-console.js +138 -0
  82. package/dist/events/predicate-element.d.ts +13 -0
  83. package/dist/events/predicate-element.js +214 -0
  84. package/dist/events/predicate-eval.d.ts +15 -3
  85. package/dist/events/predicate-eval.js +128 -110
  86. package/dist/events/predicate-parse.js +34 -1
  87. package/dist/events/predicate-request-body.d.ts +50 -0
  88. package/dist/events/predicate-request-body.js +120 -0
  89. package/dist/events/predicate-route.d.ts +21 -1
  90. package/dist/events/predicate-route.js +149 -21
  91. package/dist/events/predicate-schema.d.ts +8 -1
  92. package/dist/events/predicate-schema.js +34 -0
  93. package/dist/events/predicate.d.ts +2 -0
  94. package/dist/events/predicate.js +52 -159
  95. package/dist/events/split-text-miss.d.ts +6 -4
  96. package/dist/events/split-text-miss.js +12 -5
  97. package/dist/flows/affected-tools.js +22 -9
  98. package/dist/flows/annotate.d.ts +2 -1
  99. package/dist/flows/annotate.js +19 -1
  100. package/dist/flows/coverage.d.ts +2 -2
  101. package/dist/flows/flake.d.ts +3 -3
  102. package/dist/flows/flow-expect-grammar.d.ts +33 -0
  103. package/dist/flows/flow-expect-grammar.js +155 -0
  104. package/dist/flows/flow-replay-run.d.ts +1 -1
  105. package/dist/flows/flow-replay-run.js +28 -15
  106. package/dist/flows/flow-replay.d.ts +10 -0
  107. package/dist/flows/flow-replay.js +57 -22
  108. package/dist/flows/flow-secret-field.d.ts +14 -0
  109. package/dist/flows/flow-secret-field.js +22 -0
  110. package/dist/flows/flow-step-runners.d.ts +1 -0
  111. package/dist/flows/flow-step-runners.js +91 -34
  112. package/dist/flows/flow-success.js +22 -0
  113. package/dist/flows/flow-tools.js +27 -8
  114. package/dist/flows/flow-upload-resolve.d.ts +23 -0
  115. package/dist/flows/flow-upload-resolve.js +46 -0
  116. package/dist/flows/flows.d.ts +15 -0
  117. package/dist/flows/flows.js +17 -14
  118. package/dist/flows/git-changed.d.ts +23 -4
  119. package/dist/flows/git-changed.js +32 -6
  120. package/dist/flows/heal-run.js +2 -2
  121. package/dist/flows/predicate-to-expect.d.ts +3 -2
  122. package/dist/flows/predicate-to-expect.js +11 -5
  123. package/dist/flows/recording-backtrack.d.ts +24 -0
  124. package/dist/flows/recording-backtrack.js +73 -0
  125. package/dist/flows/recordings.d.ts +6 -0
  126. package/dist/flows/replay-halt.d.ts +26 -0
  127. package/dist/flows/replay-halt.js +30 -0
  128. package/dist/flows/replay.d.ts +0 -8
  129. package/dist/flows/replay.js +15 -3
  130. package/dist/flows/suite-flakes.d.ts +2 -1
  131. package/dist/flows/verify-change-tools.js +16 -4
  132. package/dist/honesty/already-true.d.ts +14 -0
  133. package/dist/honesty/already-true.js +47 -4
  134. package/dist/honesty/blind-spots.d.ts +3 -1
  135. package/dist/honesty/blind-spots.js +24 -1
  136. package/dist/honesty/body-capture-remedy.d.ts +42 -0
  137. package/dist/honesty/body-capture-remedy.js +95 -0
  138. package/dist/honesty/feature-capture.d.ts +3 -2
  139. package/dist/honesty/feature-capture.js +1 -1
  140. package/dist/honesty/gap-ledger.d.ts +1 -1
  141. package/dist/honesty/gap-ledger.js +1 -1
  142. package/dist/honesty/instrumentation-gaps.d.ts +8 -1
  143. package/dist/honesty/instrumentation-gaps.js +12 -2
  144. package/dist/honesty/observability.d.ts +4 -3
  145. package/dist/honesty/tool-hit-rate.d.ts +3 -2
  146. package/dist/honesty/uncaptured-bodies.d.ts +3 -1
  147. package/dist/honesty/uncaptured-bodies.js +6 -2
  148. package/dist/honesty/unsettled.d.ts +9 -0
  149. package/dist/honesty/unsettled.js +45 -0
  150. package/dist/honesty/verified.d.ts +32 -1
  151. package/dist/honesty/verified.js +37 -10
  152. package/dist/impact/impact-store.d.ts +5 -14
  153. package/dist/impact/impact-store.js +49 -6
  154. package/dist/index.js +1 -1
  155. package/dist/input/network-detail.d.ts +26 -1
  156. package/dist/input/network-detail.js +44 -12
  157. package/dist/input/network-mock-tools.js +24 -17
  158. package/dist/input/real-input.js +1 -1
  159. package/dist/input/viewport-tools.js +25 -13
  160. package/dist/intent/inline-intent.d.ts +0 -10
  161. package/dist/intent/inline-intent.js +1 -1
  162. package/dist/intent/intent-shard-store.d.ts +3 -2
  163. package/dist/intent/intent-shard-store.js +1 -1
  164. package/dist/intent/intent-shard.d.ts +14 -14
  165. package/dist/intent/intent-shard.js +4 -4
  166. package/dist/intent/intent-store.d.ts +1 -1
  167. package/dist/intent/intent-store.js +1 -1
  168. package/dist/intent/intent-subject.d.ts +2 -1
  169. package/dist/intent/open-intents.d.ts +3 -2
  170. package/dist/license/license.d.ts +4 -4
  171. package/dist/license/license.js +1 -1
  172. package/dist/mcp/attach-memory.d.ts +2 -1
  173. package/dist/mcp/client-identity.d.ts +2 -1
  174. package/dist/mcp/mcp-outage.d.ts +1 -1
  175. package/dist/mcp/mcp-outage.js +1 -1
  176. package/dist/mcp/mcp-proxy.d.ts +3 -1
  177. package/dist/mcp/mcp-proxy.js +32 -7
  178. package/dist/mcp/mcp.js +3 -1
  179. package/dist/mcp/proxy-handshake.d.ts +2 -1
  180. package/dist/mcp/proxy-idle-exit.d.ts +45 -0
  181. package/dist/mcp/proxy-idle-exit.js +94 -0
  182. package/dist/mcp/server-instructions.d.ts +2 -1
  183. package/dist/memory/project-memory.d.ts +2 -3
  184. package/dist/memory/project-memory.js +1 -1
  185. package/dist/oracles/propose-consequences.js +5 -5
  186. package/dist/oracles/self-instrument.d.ts +2 -2
  187. package/dist/oracles/self-instrument.js +1 -1
  188. package/dist/platform.d.ts +3 -5
  189. package/dist/platform.js +3 -5
  190. package/dist/pool/browser-pool.d.ts +92 -0
  191. package/dist/pool/browser-pool.js +119 -5
  192. package/dist/pool/playwright-launcher.js +29 -2
  193. package/dist/pool/storage-seed.d.ts +23 -0
  194. package/dist/pool/storage-seed.js +198 -0
  195. package/dist/project/artifact-root.d.ts +2 -1
  196. package/dist/project/memory-fs.d.ts +2 -1
  197. package/dist/project/reticle-dir.js +1 -1
  198. package/dist/project/session-root.d.ts +8 -0
  199. package/dist/project/session-root.js +9 -2
  200. package/dist/runs/reticle-runner.d.ts +14 -1
  201. package/dist/runs/reticle-runner.js +39 -2
  202. package/dist/session/attachment-history.d.ts +2 -1
  203. package/dist/session/fake-session.d.ts +37 -0
  204. package/dist/session/fake-session.js +81 -0
  205. package/dist/session/lease-visibility.d.ts +2 -1
  206. package/dist/session/live-control-tools.js +6 -0
  207. package/dist/session/no-session-diagnosis.d.ts +17 -1
  208. package/dist/session/no-session-diagnosis.js +32 -6
  209. package/dist/session/no-session-next-action.d.ts +14 -0
  210. package/dist/session/no-session-next-action.js +18 -0
  211. package/dist/session/no-session-watch.js +46 -3
  212. package/dist/session/registered-projects.d.ts +54 -0
  213. package/dist/session/registered-projects.js +73 -0
  214. package/dist/session/review-store.d.ts +2 -1
  215. package/dist/session/session-health.d.ts +62 -1
  216. package/dist/session/session-health.js +82 -2
  217. package/dist/session/session-manager.d.ts +11 -1
  218. package/dist/session/session-manager.js +18 -3
  219. package/dist/session/session-replaced.d.ts +24 -0
  220. package/dist/session/session-replaced.js +29 -0
  221. package/dist/session/session-successor.js +20 -4
  222. package/dist/session/session.d.ts +13 -5
  223. package/dist/session/session.js +31 -23
  224. package/dist/setup/agent-configs.d.ts +3 -2
  225. package/dist/setup/agent-writer.d.ts +3 -2
  226. package/dist/setup/approval-migration.d.ts +2 -1
  227. package/dist/setup/approval-migration.js +1 -1
  228. package/dist/setup/auto-approve.d.ts +11 -19
  229. package/dist/setup/auto-approve.js +15 -12
  230. package/dist/{init → setup}/confirm.js +1 -1
  231. package/dist/setup/desktop-shape.d.ts +3 -2
  232. package/dist/setup/drive-agent.d.ts +2 -3
  233. package/dist/setup/drive-agent.js +1 -1
  234. package/dist/setup/drive-plan.d.ts +3 -2
  235. package/dist/setup/ensure-daemon.d.ts +2 -1
  236. package/dist/setup/existing-app.d.ts +12 -0
  237. package/dist/setup/existing-app.js +13 -0
  238. package/dist/setup/init-host.d.ts +11 -0
  239. package/dist/setup/init-host.js +26 -0
  240. package/dist/setup/init-runtime.d.ts +4 -10
  241. package/dist/setup/init-runtime.js +23 -16
  242. package/dist/setup/license-key.d.ts +2 -1
  243. package/dist/setup/listeners.d.ts +2 -1
  244. package/dist/setup/loopback-probe-urls.d.ts +18 -0
  245. package/dist/setup/loopback-probe-urls.js +52 -0
  246. package/dist/setup/node-effects.d.ts +3 -0
  247. package/dist/setup/node-effects.js +26 -11
  248. package/dist/setup/page-probe.d.ts +5 -0
  249. package/dist/setup/relaunch.d.ts +2 -1
  250. package/dist/setup/run-setup.d.ts +5 -1
  251. package/dist/setup/run-setup.js +141 -50
  252. package/dist/setup/setup-command.d.ts +3 -2
  253. package/dist/setup/setup-command.js +8 -0
  254. package/dist/setup/setup-options.d.ts +5 -3
  255. package/dist/setup/setup-options.js +3 -2
  256. package/dist/setup/wait-progress.d.ts +28 -0
  257. package/dist/setup/wait-progress.js +43 -0
  258. package/dist/telemetry/app-instrumented.d.ts +2 -1
  259. package/dist/telemetry/app-instrumented.js +1 -1
  260. package/dist/telemetry/automation-hint.d.ts +1 -1
  261. package/dist/telemetry/automation-hint.js +1 -1
  262. package/dist/telemetry/cli-telemetry.js +1 -1
  263. package/dist/telemetry/connect-failure.d.ts +1 -1
  264. package/dist/telemetry/connect-failure.js +1 -1
  265. package/dist/telemetry/crash-cause.d.ts +3 -2
  266. package/dist/telemetry/crash-cause.js +1 -1
  267. package/dist/telemetry/daemon-telemetry.d.ts +3 -2
  268. package/dist/telemetry/daemon-telemetry.js +1 -1
  269. package/dist/telemetry/error-class.d.ts +3 -2
  270. package/dist/telemetry/error-class.js +1 -1
  271. package/dist/telemetry/feedback-cli.js +1 -1
  272. package/dist/telemetry/feedback-context.d.ts +3 -2
  273. package/dist/telemetry/feedback-context.js +5 -4
  274. package/dist/telemetry/feedback-local.d.ts +3 -2
  275. package/dist/telemetry/feedback.d.ts +4 -3
  276. package/dist/telemetry/feedback.js +1 -1
  277. package/dist/telemetry/git-facts.d.ts +2 -2
  278. package/dist/telemetry/git-facts.js +1 -1
  279. package/dist/telemetry/identify.d.ts +1 -1
  280. package/dist/telemetry/identify.js +1 -1
  281. package/dist/telemetry/init-telemetry.d.ts +2 -14
  282. package/dist/telemetry/init-telemetry.js +4 -13
  283. package/dist/telemetry/install-source.d.ts +7 -13
  284. package/dist/telemetry/install-source.js +7 -29
  285. package/dist/telemetry/license-activation.d.ts +3 -2
  286. package/dist/telemetry/license-activation.js +1 -1
  287. package/dist/telemetry/machine-snapshot.d.ts +1 -1
  288. package/dist/telemetry/mcp-connection.js +1 -1
  289. package/dist/telemetry/project-profile.d.ts +0 -14
  290. package/dist/telemetry/project-profile.js +1 -1
  291. package/dist/telemetry/proxy-telemetry.js +1 -1
  292. package/dist/telemetry/session-metrics.d.ts +1 -1
  293. package/dist/telemetry/session-metrics.js +1 -1
  294. package/dist/telemetry/telemetry.d.ts +1 -17
  295. package/dist/telemetry/telemetry.js +4 -4
  296. package/dist/telemetry/tool-refused.d.ts +1 -1
  297. package/dist/telemetry/tool-refused.js +1 -1
  298. package/dist/tools/act-observation.d.ts +3 -2
  299. package/dist/tools/act-preflight.js +8 -3
  300. package/dist/tools/act-target.d.ts +4 -0
  301. package/dist/tools/act-target.js +14 -5
  302. package/dist/tools/act-tools.d.ts +0 -2
  303. package/dist/tools/act-tools.js +45 -15
  304. package/dist/tools/assert-source.d.ts +30 -0
  305. package/dist/tools/assert-source.js +21 -0
  306. package/dist/tools/assert-verdict.d.ts +1 -1
  307. package/dist/tools/assert-verdict.js +14 -4
  308. package/dist/tools/browser-tools.js +28 -8
  309. package/dist/tools/complete-snapshot.d.ts +3 -2
  310. package/dist/tools/dynamic-tools.js +8 -0
  311. package/dist/tools/error-recovery.d.ts +10 -0
  312. package/dist/tools/error-recovery.js +23 -0
  313. package/dist/tools/feedback-tools.js +1 -1
  314. package/dist/tools/invoke-tool.js +2 -0
  315. package/dist/tools/lease-tools.d.ts +48 -20
  316. package/dist/tools/lease-tools.js +311 -76
  317. package/dist/tools/lineage-tools.d.ts +14 -0
  318. package/dist/tools/lineage-tools.js +64 -0
  319. package/dist/tools/merge-tools.js +1 -3
  320. package/dist/tools/navigate-arrival.d.ts +45 -1
  321. package/dist/tools/navigate-arrival.js +39 -6
  322. package/dist/tools/navigate-result.d.ts +8 -1
  323. package/dist/tools/navigate-result.js +14 -4
  324. package/dist/tools/numeric-bounds.d.ts +0 -11
  325. package/dist/tools/numeric-bounds.js +4 -4
  326. package/dist/tools/observe-tools.js +22 -26
  327. package/dist/tools/observed-nothing.d.ts +9 -0
  328. package/dist/tools/observed-nothing.js +2 -1
  329. package/dist/tools/read-tools.js +12 -17
  330. package/dist/tools/real-input-attempt.d.ts +14 -6
  331. package/dist/tools/real-input-attempt.js +75 -18
  332. package/dist/tools/reconcile-tools.js +2 -1
  333. package/dist/tools/reload-result.d.ts +6 -1
  334. package/dist/tools/reload-result.js +7 -2
  335. package/dist/tools/resolve-target.d.ts +7 -1
  336. package/dist/tools/resolve-target.js +72 -9
  337. package/dist/tools/settle-in-flight.d.ts +1 -1
  338. package/dist/tools/settle-in-flight.js +9 -2
  339. package/dist/tools/snapshot-delta.d.ts +24 -1
  340. package/dist/tools/snapshot-delta.js +66 -7
  341. package/dist/tools/tool-names.d.ts +1 -0
  342. package/dist/tools/tool-names.js +1 -0
  343. package/dist/tools/tool-surface.js +20 -2
  344. package/dist/tools/tools.js +120 -11
  345. package/dist/tools/verify-next-baton.d.ts +2 -1
  346. package/dist/update/sdk-sync.d.ts +1 -1
  347. package/dist/update/sdk-sync.js +1 -1
  348. package/dist/update/updater.js +1 -1
  349. package/dist/version/sdk-fix.d.ts +2 -1
  350. package/dist/version/version-skew.d.ts +0 -8
  351. package/dist/version/version-skew.js +1 -1
  352. package/docs/README.md +8 -6
  353. package/docs/actions.mdx +1 -1
  354. package/docs/agent-cheatsheet.md +4 -2
  355. package/docs/architecture.md +1 -1
  356. package/docs/cli/mcp.mdx +3 -1
  357. package/docs/cli/watch.mdx +1 -1
  358. package/docs/cli.mdx +1 -1
  359. package/docs/desktop.mdx +1 -1
  360. package/docs/docs.json +8 -3
  361. package/docs/faq.mdx +9 -0
  362. package/docs/first-drive.md +136 -0
  363. package/docs/frameworks.mdx +29 -7
  364. package/docs/gate-plan.md +2 -2
  365. package/docs/gates.md +19 -11
  366. package/docs/getting-started.md +7 -1
  367. package/docs/http-transport.md +164 -0
  368. package/docs/index.mdx +5 -3
  369. package/docs/install-manual.mdx +1 -1
  370. package/docs/packages/tauri.mdx +1 -1
  371. package/docs/packages.mdx +15 -3
  372. package/docs/platform-integration.md +1 -1
  373. package/docs/predicates.mdx +2 -0
  374. package/docs/system-map.md +2 -2
  375. package/docs/telemetry-contract.md +4 -4
  376. package/docs/telemetry-events.mdx +1 -1
  377. package/docs/telemetry.md +3 -3
  378. package/docs/tools-navigate.mdx +14 -6
  379. package/docs/usage.md +4 -4
  380. package/package.json +7 -4
  381. package/dist/init/agent-rules.d.ts +0 -116
  382. package/dist/init/agent-rules.js +0 -279
  383. package/dist/init/app-choice.d.ts +0 -20
  384. package/dist/init/app-choice.js +0 -25
  385. package/dist/init/astro-host.d.ts +0 -21
  386. package/dist/init/astro-host.js +0 -27
  387. package/dist/init/astro-patch.d.ts +0 -33
  388. package/dist/init/astro-patch.js +0 -300
  389. package/dist/init/capabilities.d.ts +0 -40
  390. package/dist/init/capabilities.js +0 -150
  391. package/dist/init/closing-hint.d.ts +0 -36
  392. package/dist/init/closing-hint.js +0 -120
  393. package/dist/init/connect-steps.d.ts +0 -2
  394. package/dist/init/connect-steps.js +0 -50
  395. package/dist/init/cra.d.ts +0 -79
  396. package/dist/init/cra.js +0 -178
  397. package/dist/init/csp-check.d.ts +0 -38
  398. package/dist/init/csp-check.js +0 -107
  399. package/dist/init/csp-doctor.d.ts +0 -32
  400. package/dist/init/csp-doctor.js +0 -54
  401. package/dist/init/desktop-doctor.d.ts +0 -41
  402. package/dist/init/desktop-doctor.js +0 -198
  403. package/dist/init/detect.d.ts +0 -96
  404. package/dist/init/detect.js +0 -209
  405. package/dist/init/dev-script.d.ts +0 -50
  406. package/dist/init/dev-script.js +0 -66
  407. package/dist/init/format-generated.d.ts +0 -18
  408. package/dist/init/format-generated.js +0 -39
  409. package/dist/init/install-hint.d.ts +0 -9
  410. package/dist/init/install-hint.js +0 -29
  411. package/dist/init/mcp-clients.d.ts +0 -89
  412. package/dist/init/mcp-clients.js +0 -299
  413. package/dist/init/mcp-registered.d.ts +0 -12
  414. package/dist/init/mcp-registered.js +0 -14
  415. package/dist/init/mcp.d.ts +0 -89
  416. package/dist/init/mcp.js +0 -129
  417. package/dist/init/next-patch.d.ts +0 -39
  418. package/dist/init/next-patch.js +0 -196
  419. package/dist/init/node-io.d.ts +0 -7
  420. package/dist/init/node-io.js +0 -117
  421. package/dist/init/non-js-project.d.ts +0 -27
  422. package/dist/init/non-js-project.js +0 -84
  423. package/dist/init/patch-kind.d.ts +0 -20
  424. package/dist/init/patch-kind.js +0 -10
  425. package/dist/init/plan-framework.d.ts +0 -64
  426. package/dist/init/plan-framework.js +0 -565
  427. package/dist/init/plan.d.ts +0 -242
  428. package/dist/init/plan.js +0 -703
  429. package/dist/init/preflight.d.ts +0 -28
  430. package/dist/init/preflight.js +0 -38
  431. package/dist/init/project-id.d.ts +0 -12
  432. package/dist/init/project-id.js +0 -25
  433. package/dist/init/refresh-rules.d.ts +0 -39
  434. package/dist/init/refresh-rules.js +0 -51
  435. package/dist/init/run.d.ts +0 -117
  436. package/dist/init/run.js +0 -806
  437. package/dist/init/slash-command.d.ts +0 -27
  438. package/dist/init/slash-command.js +0 -132
  439. package/dist/init/snippets.d.ts +0 -134
  440. package/dist/init/snippets.js +0 -572
  441. package/dist/init/vite-config.d.ts +0 -18
  442. package/dist/init/vite-config.js +0 -114
  443. package/dist/init/windows-quote.d.ts +0 -38
  444. package/dist/init/windows-quote.js +0 -70
  445. package/dist/init/workspace-apps.d.ts +0 -30
  446. package/dist/init/workspace-apps.js +0 -178
  447. package/dist/project/remember-project.d.ts +0 -24
  448. package/dist/project/remember-project.js +0 -40
  449. /package/dist/{init → setup}/confirm.d.ts +0 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Where a runtime value came from: state path ← the signal that preceded it ← the request behind it.
3
+ *
4
+ * The prior art this issue names (`firatorhan/sentinel`'s `get_lineage`) builds the same chain by
5
+ * picking "the most recent action whose payload contains this value", then "an API call whose type
6
+ * matches". That is CORRELATION, and for a devtool a human reads it is the right trade — the reader
7
+ * discounts it on sight.
8
+ *
9
+ * Reticle's output is read by an agent that acts on it, so the same chain presented as fact is the
10
+ * false-green shape this project exists to refuse. The join here is deliberately no cleverer than
11
+ * theirs; what is different is that every link carries how much it is worth:
12
+ *
13
+ * - a state path that changed is OBSERVED — we have the event;
14
+ * - "this signal caused it" is INFERRED, and the wording says so;
15
+ * - several candidates are NAMED rather than narrowed to the most recent, because choosing is the
16
+ * one thing a correlation cannot justify;
17
+ * - no candidate says the value was not seen to pass through one, and reaches for nothing.
18
+ *
19
+ * Read-only. Produces no verdict, is not reachable from an assert path, and changes nothing.
20
+ */
21
+ import { EventType } from '@reticlehq/core';
22
+ import { asString } from '../tools/tools-helpers.js';
23
+ /**
24
+ * How far back to look for a cause.
25
+ *
26
+ * Wide enough for a fetch → dispatch → render sequence, narrow enough that an unrelated signal a
27
+ * page-load earlier cannot be offered as a candidate. Not a correctness bound: widening it produces
28
+ * MORE named candidates, never a wrong single answer, because ambiguity is reported rather than
29
+ * resolved.
30
+ */
31
+ const CAUSE_WINDOW_MS = 5_000;
32
+ function stateChangesFor(events, query) {
33
+ return events.filter((e) => {
34
+ if (e.type !== EventType.STATE_CHANGE)
35
+ return false;
36
+ const path = asString(e.data['path']);
37
+ if (path === undefined || !path.includes(query.path))
38
+ return false;
39
+ if (query.value === undefined)
40
+ return true;
41
+ return String(e.data['value']) === query.value;
42
+ });
43
+ }
44
+ /** Events of one kind inside the causal window before `t`, oldest first. */
45
+ function precursors(events, type, t) {
46
+ return events.filter((e) => e.type === type && e.t < t && e.t >= t - CAUSE_WINDOW_MS);
47
+ }
48
+ /**
49
+ * Render an inferred link, hedged according to how many things could explain it.
50
+ *
51
+ * The wording IS the deliverable. `← USER_FETCH_SUCCESS` reads as fact; the two forms below read as
52
+ * what they are, and a reader who skims still sees the hedge because it is in the first few words.
53
+ */
54
+ function inferredLink(kind, labels) {
55
+ const one = labels[0] ?? '';
56
+ if (1 === labels.length) {
57
+ return { kind, text: `← likely ${one} (inferred from timing, 1 candidate)`, observed: false };
58
+ }
59
+ return {
60
+ kind,
61
+ text: `← ambiguous: ${String(labels.length)} candidates (${labels.join(', ')}) — not resolved, because timing alone cannot choose between them`,
62
+ observed: false,
63
+ candidates: labels,
64
+ };
65
+ }
66
+ function signalLabel(e) {
67
+ return asString(e.data['name']) ?? '(unnamed signal)';
68
+ }
69
+ function netLabel(e) {
70
+ const method = asString(e.data['method']) ?? 'GET';
71
+ const url = asString(e.data['url']) ?? '(unknown url)';
72
+ const status = e.data['status'];
73
+ return `${method} ${url}${'number' === typeof status ? ` (${String(status)})` : ''}`;
74
+ }
75
+ /**
76
+ * The chain, most recent change first.
77
+ *
78
+ * Stops at the first link with nothing to point at, and says why. Walking past a gap would mean
79
+ * joining across a hole in the evidence, which is exactly the step that turns a correlation into a
80
+ * claim.
81
+ */
82
+ export function traceLineage(events, query) {
83
+ const changes = stateChangesFor(events, query);
84
+ const latest = changes[changes.length - 1];
85
+ if (latest === undefined) {
86
+ return {
87
+ found: false,
88
+ chain: [],
89
+ note: `'${query.path}' was never seen to change in this window, so there is nothing to trace back from. It may be set before Reticle connected, or held somewhere no registered store exposes.`,
90
+ };
91
+ }
92
+ const observedValue = String(latest.data['value']);
93
+ const chain = [
94
+ {
95
+ kind: 'state',
96
+ text: `${asString(latest.data['path']) ?? query.path} = ${observedValue} (observed: state change at ${String(latest.t)}ms)`,
97
+ observed: true,
98
+ },
99
+ ];
100
+ const signals = precursors(events, EventType.SIGNAL, latest.t);
101
+ if (0 === signals.length) {
102
+ return {
103
+ found: true,
104
+ chain,
105
+ note: 'No signal fired in the window before this change, so the value was not seen to pass through one. Nothing further is claimed — the change is real, its cause is not in evidence.',
106
+ };
107
+ }
108
+ chain.push(inferredLink('signal', signals.map(signalLabel)));
109
+ // Anchored on the OLDEST candidate signal: a request that preceded any of them could have produced
110
+ // it, and narrowing to the newest would quietly discard candidates for the link below.
111
+ const anchor = signals[0]?.t ?? latest.t;
112
+ const calls = precursors(events, EventType.NET_REQUEST, anchor);
113
+ if (0 === calls.length) {
114
+ return {
115
+ found: true,
116
+ chain,
117
+ note: 'No request preceded that signal in the window, so the chain stops there rather than joining across a gap in the evidence.',
118
+ };
119
+ }
120
+ chain.push(inferredLink('net', calls.map(netLabel)));
121
+ return { found: true, chain };
122
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * What a network verdict returns as its PROOF.
3
+ *
4
+ * Split out of predicate-eval.ts, which sits at the size backstop, and split HERE because this is a
5
+ * separate question from "did the predicate hold": that file decides the answer, this one decides
6
+ * what is shown to justify it. The two failure modes differ too. A wrong verdict is a wrong verdict;
7
+ * wrong evidence is a verdict nobody can check, or one that costs forty times what it is worth.
8
+ */
9
+ import { PredicateKind } from '@reticlehq/core';
10
+ import type { Predicate } from './predicate-schema.js';
11
+ /**
12
+ * The matched call, as the proof of the claim that was actually made.
13
+ *
14
+ * A predicate asking about status and url is proved by status and url; returning the whole payload
15
+ * beside them is cost with no bearing on the verdict. A predicate asking `bodyContains` is proved BY
16
+ * the body, and that one keeps it in full — clipping a body a check consumed would be weakening the
17
+ * check to make it cheaper, which is the trade this codebase does not make.
18
+ */
19
+ export declare function netEvidence(data: Record<string, unknown>, p: Extract<Predicate, {
20
+ kind: typeof PredicateKind.NET;
21
+ }>): unknown;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * What a network verdict returns as its PROOF.
3
+ *
4
+ * Split out of predicate-eval.ts, which sits at the size backstop, and split HERE because this is a
5
+ * separate question from "did the predicate hold": that file decides the answer, this one decides
6
+ * what is shown to justify it. The two failure modes differ too. A wrong verdict is a wrong verdict;
7
+ * wrong evidence is a verdict nobody can check, or one that costs forty times what it is worth.
8
+ */
9
+ import { PredicateKind } from '@reticlehq/core';
10
+ import { withoutUrlRaw } from './event-filters.js';
11
+ /**
12
+ * How much of a body the evidence for a PASS may carry when the check never read it.
13
+ *
14
+ * Enough to see the shape of what the server answered — a JSON document's opening keys — and not the
15
+ * document itself. Measured in the field: six `act_and_wait` calls asserting `{ urlContains, status
16
+ * 200 }` through one interview each returned the server's whole answer, ~9.6KB of the same document
17
+ * growing by one slot per step, so almost all of it was a re-send of what the agent already had. The
18
+ * evidence block was 9,693 of an 11,194-byte result.
19
+ *
20
+ * Larger than `MAX_BODY_IN_FAILURE`, and deliberately: a failure quotes the value that DIFFERED and
21
+ * knows which one that is, while a pass is showing the caller what came back and has no such anchor.
22
+ */
23
+ const MAX_BODY_IN_EVIDENCE = 512;
24
+ /** Body fields a net predicate can read, and the clause that reads each. */
25
+ const BODY_EVIDENCE_FIELDS = {
26
+ responseBody: 'bodyContains',
27
+ requestBody: 'requestBodyContains',
28
+ };
29
+ /**
30
+ * How much of the body around a matched needle is quoted.
31
+ *
32
+ * Enough to see the needle in its structure — the key it sits under, the value beside it — without
33
+ * the document it came from.
34
+ */
35
+ const BODY_MATCH_CONTEXT = 120;
36
+ /** Where a quoted window starts, and the elision markers that keep it honest at both ends. */
37
+ function windowAround(body, at, needle) {
38
+ const from = Math.max(0, at - BODY_MATCH_CONTEXT);
39
+ const to = Math.min(body.length, at + needle.length + BODY_MATCH_CONTEXT);
40
+ return `${from > 0 ? '…' : ''}${body.slice(from, to)}${to < body.length ? '…' : ''}`;
41
+ }
42
+ /** The head of a body, for a claim that never read it. Elision is VISIBLE — see the note below. */
43
+ function headOf(body) {
44
+ return body.length <= MAX_BODY_IN_EVIDENCE ? body : `${body.slice(0, MAX_BODY_IN_EVIDENCE)}…`;
45
+ }
46
+ /**
47
+ * The matched call, as the proof of the claim that was actually made.
48
+ *
49
+ * A predicate asking about status and url is proved by status and url; returning the whole payload
50
+ * beside them is cost with no bearing on the verdict. A predicate asking `bodyContains` is proved BY
51
+ * the body, and that one keeps it in full — clipping a body a check consumed would be weakening the
52
+ * check to make it cheaper, which is the trade this codebase does not make.
53
+ */
54
+ export function netEvidence(data, p) {
55
+ const stripped = withoutUrlRaw({ data }).data;
56
+ if (null === stripped || 'object' !== typeof stripped)
57
+ return stripped;
58
+ const clause = p;
59
+ const out = { ...stripped };
60
+ for (const [field, askedBy] of Object.entries(BODY_EVIDENCE_FIELDS)) {
61
+ const body = out[field];
62
+ if ('string' !== typeof body || body.length <= MAX_BODY_IN_EVIDENCE)
63
+ continue;
64
+ const needle = clause[askedBy];
65
+ const at = 'string' === typeof needle ? body.indexOf(needle) : -1;
66
+ if (at < 0) {
67
+ // No clause read this body, or the pass came from elsewhere: the head shows the shape of the
68
+ // answer. The ellipsis is load-bearing — a silently shortened body reads as "the server
69
+ // answered this much", a different and wrong fact — and `responseSize` rides alongside
70
+ // untouched, so the real length stays available and `reticle_network` fetches the rest.
71
+ out[field] = headOf(body);
72
+ continue;
73
+ }
74
+ // The clause matched. What proves it is the MATCH, not the document it was found in: the needle
75
+ // in its context, plus where it was. Six field verdicts each returned the same 8KB interview
76
+ // document to prove one substring of it, and the document proved nothing the window does not.
77
+ out[field] = windowAround(body, at, needle);
78
+ out['bodyMatchAt'] = at;
79
+ }
80
+ return out;
81
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Console predicates: the assertion agents lean on hardest, and the one with a structural blind spot.
3
+ *
4
+ * Split out of `predicate-eval.ts` when it crossed the file cap. Cohesive on its own terms — the
5
+ * level map, the channel-liveness gate, and the absent/present branches are one subject, and nothing
6
+ * else in the evaluator reads a CONSOLE_* event.
7
+ */
8
+ import { PredicateKind, type ReticleEvent } from '@reticlehq/core';
9
+ import type { EvalResult } from './predicate-eval.js';
10
+ import type { Predicate } from './predicate-schema.js';
11
+ export declare function evalConsole(events: ReticleEvent[], p: Extract<Predicate, {
12
+ kind: typeof PredicateKind.CONSOLE;
13
+ }>): EvalResult;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Console predicates: the assertion agents lean on hardest, and the one with a structural blind spot.
3
+ *
4
+ * Split out of `predicate-eval.ts` when it crossed the file cap. Cohesive on its own terms — the
5
+ * level map, the channel-liveness gate, and the absent/present branches are one subject, and nothing
6
+ * else in the evaluator reads a CONSOLE_* event.
7
+ */
8
+ import { CONSOLE_ATTACH_NOTE, EventType, PredicateKind } from '@reticlehq/core';
9
+ /** The only console levels Reticle instruments (console.info/debug/trace are NOT patched). */
10
+ const CONSOLE_LEVEL_TYPE = {
11
+ log: EventType.CONSOLE_LOG,
12
+ warn: EventType.CONSOLE_WARN,
13
+ error: EventType.CONSOLE_ERROR,
14
+ };
15
+ /**
16
+ * Has the console channel reported ANYTHING in this window, at any level?
17
+ *
18
+ * Liveness, on the same argument `observerSawSubresources` makes for resource timing: one entry
19
+ * anywhere proves the channel reaches this document, so the caller is not looking at a dead read.
20
+ * Any level counts — a `console.log` proves the patch is in place just as well as an error would,
21
+ * and asking only about the ASSERTED level would re-fire the caveat on every quiet window of a
22
+ * chatty page, which is a toll paid where the warning is not true.
23
+ *
24
+ * It does not CLOSE the blind stretch — that is about time before attach and cannot be closed from
25
+ * this side. It only rules out the reading the caveat's wording would get wrong.
26
+ */
27
+ function channelReported(events, since) {
28
+ return events.some((e) => e.t >= since &&
29
+ (e.type === EventType.CONSOLE_LOG ||
30
+ e.type === EventType.CONSOLE_WARN ||
31
+ e.type === EventType.CONSOLE_ERROR ||
32
+ e.type === EventType.CONSOLE_INFO ||
33
+ e.type === EventType.CONSOLE_DEBUG ||
34
+ e.type === EventType.ERROR_UNCAUGHT));
35
+ }
36
+ export function evalConsole(events, p) {
37
+ const since = p.since ?? 0;
38
+ // Reticle only instruments console.log/warn/error. A level outside that set is never captured,
39
+ // so its events can't exist — and an `absent` assertion on it would verify NOTHING while
40
+ // reporting green. Fail loudly instead of false-passing.
41
+ if (p.level !== undefined && p.level !== 'error' && CONSOLE_LEVEL_TYPE[p.level] === undefined) {
42
+ return {
43
+ pass: false,
44
+ failureReason: `console level '${p.level}' is not captured — Reticle instruments console.log, console.warn, console.error only`,
45
+ observed: `level '${p.level}' is not instrumented, so no event of it can ever exist`,
46
+ expected: 'a level Reticle captures: log, warn, or error',
47
+ assertion: 'console.uninstrumented-level',
48
+ };
49
+ }
50
+ const matches = events.filter((e) => {
51
+ if (e.t < since)
52
+ return false;
53
+ const isErr = e.type === EventType.CONSOLE_ERROR || e.type === EventType.ERROR_UNCAUGHT;
54
+ if (p.level === undefined) {
55
+ return (e.type === EventType.CONSOLE_LOG ||
56
+ e.type === EventType.CONSOLE_WARN ||
57
+ e.type === EventType.CONSOLE_ERROR ||
58
+ e.type === EventType.ERROR_UNCAUGHT);
59
+ }
60
+ if ('error' === p.level)
61
+ return isErr;
62
+ return e.type === CONSOLE_LEVEL_TYPE[p.level];
63
+ });
64
+ // A text match narrows the population to entries whose captured message contains the substring.
65
+ // With `absent: true` this is the whole point: "THIS message did not appear", not "no messages
66
+ // appeared" — the difference between a regression check and a fragile one that any unrelated
67
+ // warning anywhere in the app breaks.
68
+ const wanted = 'contains' in p ? p.contains : undefined;
69
+ // Captured messages are strings (stringifyArgs in the browser observer), but a malformed or
70
+ // foreign event must not crash the evaluator: non-strings stringify defensively, and objects
71
+ // go through JSON.stringify rather than a default toString that would print '[object Object]'.
72
+ const asText = (v) => {
73
+ if ('string' === typeof v)
74
+ return v;
75
+ try {
76
+ return JSON.stringify(v) ?? '';
77
+ }
78
+ catch {
79
+ return '';
80
+ }
81
+ };
82
+ const matching = wanted !== undefined
83
+ ? matches.filter((e) => asText(e.data['message']).includes(wanted))
84
+ : matches;
85
+ if (true === p.absent) {
86
+ if (wanted !== undefined && 0 === matching.length && matches.length > 0) {
87
+ // Other entries exist but none carries the substring: exactly the pass an absence-with-match
88
+ // asserts. Name both counts so the caller can tell this from a silent window.
89
+ return {
90
+ pass: true,
91
+ evidence: { absent: true, contains: wanted },
92
+ };
93
+ }
94
+ return 0 === matching.length
95
+ ? {
96
+ pass: true,
97
+ evidence: {
98
+ absent: true,
99
+ // Only when the window starts at attach AND nothing came through at all. An entry
100
+ // anywhere in the window proves the channel reaches this document, which is the case
101
+ // the caveat's wording would misdescribe; a window an action opened has `since > 0` and
102
+ // was fully observed. Costs nothing on every other pass, which is most of them.
103
+ ...(0 === since && !channelReported(events, since)
104
+ ? { unobservedBefore: CONSOLE_ATTACH_NOTE }
105
+ : {}),
106
+ },
107
+ }
108
+ : {
109
+ pass: false,
110
+ failureReason: wanted !== undefined
111
+ ? `expected no ${p.level ?? 'console'} entry containing ${JSON.stringify(wanted)} but found ${String(matching.length)}`
112
+ : `expected no ${p.level ?? 'console'} entries but found ${String(matches.length)}`,
113
+ observed: wanted !== undefined
114
+ ? `${String(matching.length)} ${p.level ?? 'console'} entr${1 === matching.length ? 'y' : 'ies'} containing ${JSON.stringify(wanted)}`
115
+ : `${String(matches.length)} ${p.level ?? 'console'} entr${1 === matches.length ? 'y' : 'ies'}`,
116
+ expected: wanted !== undefined
117
+ ? `no ${p.level ?? 'console'} entry containing ${JSON.stringify(wanted)}`
118
+ : `no ${p.level ?? 'console'} entries`,
119
+ assertion: wanted !== undefined ? 'console.absent-contains' : 'console.absent',
120
+ evidence: matching.map((e) => e.data),
121
+ };
122
+ }
123
+ return matching.length > 0
124
+ ? { pass: true, evidence: matching.map((e) => e.data) }
125
+ : {
126
+ pass: false,
127
+ failureReason: wanted !== undefined
128
+ ? `no ${p.level ?? 'console'} entry containing ${JSON.stringify(wanted)} found`
129
+ : `no ${p.level ?? 'console'} entries found`,
130
+ observed: wanted !== undefined
131
+ ? `no ${p.level ?? 'console'} entry containing ${JSON.stringify(wanted)} in the window`
132
+ : `no ${p.level ?? 'console'} entries in the window`,
133
+ expected: wanted !== undefined
134
+ ? `at least one ${p.level ?? 'console'} entry containing ${JSON.stringify(wanted)}`
135
+ : `at least one ${p.level ?? 'console'} entry`,
136
+ assertion: 'console.present',
137
+ };
138
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Element and text predicates: everything that resolves a locator against the live DOM.
3
+ *
4
+ * Split out of `predicate.ts` when it crossed the file cap. It is a cohesive unit on its own terms —
5
+ * one round-trip protocol (`matchOnce`), the fields the browser locator drops and this side enforces
6
+ * back (`residualQueryChecks`), the absent/present asymmetry, and the near-miss diagnostics that only
7
+ * ever enrich a failure. Nothing else in the predicate layer talks to `ReticleCommand.MATCH`.
8
+ */
9
+ import { type ElementQuery, type ElementState, type MatchResult } from '@reticlehq/core';
10
+ import { type EvalResult } from './predicate-eval.js';
11
+ import type { PredicateSession } from './predicate.js';
12
+ export declare function matchOnce(session: PredicateSession, query: ElementQuery, state: ElementState | undefined): Promise<MatchResult>;
13
+ export declare function evalElement(session: PredicateSession, query: ElementQuery, state: ElementState | undefined, absent: boolean, diagnose: boolean): Promise<EvalResult>;
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Element and text predicates: everything that resolves a locator against the live DOM.
3
+ *
4
+ * Split out of `predicate.ts` when it crossed the file cap. It is a cohesive unit on its own terms —
5
+ * one round-trip protocol (`matchOnce`), the fields the browser locator drops and this side enforces
6
+ * back (`residualQueryChecks`), the absent/present asymmetry, and the near-miss diagnostics that only
7
+ * ever enrich a failure. Nothing else in the predicate layer talks to `ReticleCommand.MATCH`.
8
+ */
9
+ import { ReticleCommand, } from '@reticlehq/core';
10
+ import { residualQueryChecks, satisfiesResiduals, describeResidual, } from './predicate-eval.js';
11
+ import { describeTestidMiss } from './testid-near-miss.js';
12
+ import { describeSplitTextMiss } from './split-text-miss.js';
13
+ export async function matchOnce(session, query, state) {
14
+ const res = await session.command(ReticleCommand.MATCH, { query, state });
15
+ if (!res.ok)
16
+ return { matched: false, count: 0, elements: [] };
17
+ return (res.result ?? { matched: false, count: 0, elements: [] });
18
+ }
19
+ /**
20
+ * Roles that exist to ANNOUNCE something. An empty one is the container, not the message.
21
+ *
22
+ * Every toast and notification library mounts its live region at boot and never removes it, so a
23
+ * bare `{ role: "alert" }` presence check is satisfied on those apps with nothing on screen — it
24
+ * cannot fail, and a predicate that cannot fail is not a check. Restricted to these roles because
25
+ * they are the ones whose whole purpose is content: an unnamed `img`, `separator` or `progressbar`
26
+ * is an ordinary thing to assert the presence of, and is left alone.
27
+ */
28
+ const ANNOUNCEMENT_ROLES = new Set([
29
+ 'alert',
30
+ 'alertdialog',
31
+ 'status',
32
+ 'log',
33
+ 'marquee',
34
+ 'timer',
35
+ ]);
36
+ /**
37
+ * The matches that actually carry content, for a query that named none of its own.
38
+ *
39
+ * A `name`, `text` or `testid` in the query is already a specific claim and is never second-guessed
40
+ * here — this only fills the gap left by a query that asked for a role and nothing else.
41
+ */
42
+ function substantiveMatches(query, elements) {
43
+ const bareRole = query.role !== undefined &&
44
+ query.name === undefined &&
45
+ query.text === undefined &&
46
+ query.testid === undefined;
47
+ if (!bareRole || !ANNOUNCEMENT_ROLES.has(query.role ?? ''))
48
+ return elements;
49
+ // Nothing described, nothing to judge. `elements` is only the described PREFIX of a match — the
50
+ // browser truncates it, and it can be empty while `count` is not. Calling that hollow would turn a
51
+ // truncation into a verdict, so an unjudgeable set keeps the old answer.
52
+ if (0 === elements.length)
53
+ return elements;
54
+ return elements.filter((e) => e.name.length > 0 || (e.text ?? '').trim().length > 0);
55
+ }
56
+ export async function evalElement(session, query, state, absent, diagnose) {
57
+ // Fields the browser's locator would have DROPPED, enforced back here — see residualQueryChecks.
58
+ // Checked before the round-trip when nothing can enforce them: a predicate that cannot be evaluated
59
+ // must say so rather than resolve to whatever the surviving half of it happened to match.
60
+ const residual = residualQueryChecks(query);
61
+ if (residual.unusable.length > 0) {
62
+ const reason = `the element locator ignores ${residual.unusable.map((f) => `\`${f}\``).join(', ')} ` +
63
+ `in ${JSON.stringify(query)} — it resolves by the first of by+value, component/source, role, ` +
64
+ 'text, label, placeholder, testid, alt that is present, and nothing here can check the rest. ' +
65
+ 'Assert them one locator at a time, or move the extra field into the locator';
66
+ return { pass: false, failureReason: reason, inconclusive: reason };
67
+ }
68
+ let match = await matchOnce(session, query, state);
69
+ const subject = JSON.stringify(query);
70
+ // A residual narrows the SET; `count` is every match while `elements` is only the described prefix,
71
+ // so a locator broad enough to be truncated cannot be narrowed honestly. Say so instead of guessing.
72
+ if (residual.checks.length > 0 && match.count > match.elements.length) {
73
+ const reason = `${String(match.count)} elements matched ${subject} and only ${String(match.elements.length)} were described, so ${residual.checks.map(([f]) => `\`${f}\``).join(', ')} could not be checked against all of them — narrow the locator`;
74
+ return { pass: false, failureReason: reason, inconclusive: reason };
75
+ }
76
+ const kept = match.elements.filter((element) => satisfiesResiduals(element, residual.checks));
77
+ // The locator found something and the dropped fields disagree with it. Reported separately from a
78
+ // plain miss because the fixes are opposite: the element IS there, its value is not what was claimed.
79
+ if (residual.checks.length > 0 && match.matched && 0 === kept.length && !absent) {
80
+ const wanted = residual.checks.map(([f, want]) => `${f}=${JSON.stringify(want)}`).join(', ');
81
+ return {
82
+ pass: false,
83
+ failureReason: `element matching ${subject} is present but ${wanted} does not hold`,
84
+ observed: match.elements
85
+ .map((element) => residual.checks.map(([f]) => describeResidual(element, f)).join(', '))
86
+ .join('; '),
87
+ expected: `an element matching ${subject} with ${wanted}`,
88
+ assertion: `element.${residual.checks[0]?.[0] ?? 'residual'}`,
89
+ evidence: match.elements,
90
+ };
91
+ }
92
+ if (residual.checks.length > 0) {
93
+ match = { ...match, matched: kept.length > 0, count: kept.length, elements: kept };
94
+ }
95
+ // A given-but-missing scope is handled ASYMMETRICALLY, because "absent" and "present" ask different
96
+ // questions of a scope that no longer exists:
97
+ // - ABSENT: an element is trivially absent from a container that isn't there. This is also the
98
+ // everyday "wait for the #overlay/#spinner/#modal to disappear" pattern (scope the wait to the
99
+ // node being removed) — treating scopeMissing as a hard fail there burned the whole timeout and
100
+ // flipped a correct green to red. So scopeMissing satisfies an absence check.
101
+ // - PRESENT: you cannot confirm an element is present inside a scope that resolved to nothing, and
102
+ // silently widening to the whole page is the original false green. So scopeMissing FAILS presence
103
+ // (on the wait_for path this just keeps polling until the scope appears).
104
+ if (absent) {
105
+ if (true === match.scopeMissing) {
106
+ return { pass: true, evidence: { absent: true, scopeMissing: true } };
107
+ }
108
+ return match.matched
109
+ ? {
110
+ pass: false,
111
+ failureReason: `expected element to be absent but found ${String(match.count)}`,
112
+ observed: `${String(match.count)} element(s) matching ${subject}`,
113
+ expected: `no element matching ${subject}`,
114
+ assertion: 'element.absent',
115
+ evidence: match.elements,
116
+ }
117
+ : { pass: true, evidence: { absent: true } };
118
+ }
119
+ if (true === match.scopeMissing) {
120
+ return {
121
+ pass: false,
122
+ failureReason: `scope resolved to no element — cannot confirm ${subject} is present`,
123
+ observed: 'the requested scope is not on the page (unmounted or selector matched nothing)',
124
+ expected: `an element matching ${subject} within an existing scope`,
125
+ assertion: 'element.present',
126
+ evidence: { scopeMissing: true },
127
+ };
128
+ }
129
+ const announced = substantiveMatches(query, match.elements);
130
+ if (match.matched && (announced.length > 0 || 0 === match.elements.length)) {
131
+ return { pass: true, evidence: announced };
132
+ }
133
+ // Matched, but every match was a mounted-and-empty live region. Reported as its own failure rather
134
+ // than the plain miss below, because the fixes are opposite: the container IS on the page, so
135
+ // "no element matched" would send the caller looking for a render that already happened.
136
+ if (match.matched) {
137
+ return {
138
+ pass: false,
139
+ failureReason: `${String(match.count)} element(s) matched ${subject}, and every one is empty — ` +
140
+ 'a live region mounted with no name and no text has announced nothing. Assert the ' +
141
+ 'message itself (a `name` or `text` in the query) rather than the container',
142
+ observed: `${String(match.count)} empty '${query.role ?? ''}' region(s) with no name or text`,
143
+ expected: `a '${query.role ?? ''}' carrying a name or rendered text`,
144
+ assertion: 'element.announced',
145
+ evidence: match.elements,
146
+ };
147
+ }
148
+ // The near-miss diagnostic below costs one or two EXTRA MATCH round-trips. It only enriches a FAILED
149
+ // verdict, and a wait loop's interim rechecks read nothing but `pass` — so on the poll path (diagnose
150
+ // false) skip straight to the plain fail. Under an event flood a role+name element wait was firing
151
+ // two live-DOM scans per recheck for a diagnostic no interim eval ever reads; the final timeout eval
152
+ // still runs with diagnose=true and produces the full near-miss.
153
+ if (!diagnose) {
154
+ return {
155
+ pass: false,
156
+ failureReason: `no element matched ${subject}${state === undefined ? '' : ` in state '${state}'`}`,
157
+ observed: 'no matching element on the page',
158
+ expected: `an element matching ${subject}${state === undefined ? '' : ` in state '${state}'`}`,
159
+ assertion: 'element.present',
160
+ };
161
+ }
162
+ // Diagnostic near-miss: was it there but in the wrong state, or a similar element present?
163
+ if (state !== undefined) {
164
+ const relaxed = await matchOnce(session, query, undefined);
165
+ if (relaxed.matched) {
166
+ return {
167
+ pass: false,
168
+ failureReason: `element exists but not in state '${state}'`,
169
+ observed: `element matching ${subject} is present, states: ${relaxed.elements[0]?.states.join(', ') ?? 'unknown'}`,
170
+ expected: `element matching ${subject} in state '${state}'`,
171
+ assertion: 'element.state',
172
+ evidence: { nearMiss: relaxed.elements },
173
+ };
174
+ }
175
+ }
176
+ if (query.role !== undefined && query.name !== undefined) {
177
+ const roleOnly = await matchOnce(session, { role: query.role }, state);
178
+ if (roleOnly.matched) {
179
+ return {
180
+ pass: false,
181
+ failureReason: `no '${query.role}' named '${query.name}'; saw: ${roleOnly.elements
182
+ .map((e) => e.name)
183
+ .filter((n) => n.length > 0)
184
+ .join(', ')}`,
185
+ observed: `${String(roleOnly.count)} '${query.role}' element(s), named: ${roleOnly.elements
186
+ .map((e) => e.name)
187
+ .filter((n) => n.length > 0)
188
+ .join(', ')}`,
189
+ expected: `a '${query.role}' named '${query.name}'`,
190
+ assertion: 'element.role+name',
191
+ evidence: { nearMiss: roleOnly.elements },
192
+ };
193
+ }
194
+ }
195
+ // The testid near-miss: name what IS here, so a typo is one step from fixed rather than a dead
196
+ // end. reticle_query has always done this; the predicate path had no equivalent. See
197
+ // testid-near-miss.ts.
198
+ const present = match.hint?.presentTestids ?? [];
199
+ const alsoHere = query.testid === undefined ? undefined : describeTestidMiss(query.testid, present);
200
+ // A text miss where the string is on the page but split across children reads exactly like an
201
+ // element that never rendered. Naming the container is the difference between a retry and a bug
202
+ // report against working code. See split-text-miss.ts.
203
+ const splitText = describeSplitTextMiss(match.hint?.splitText, query.text);
204
+ const clause = splitText ?? (alsoHere === undefined || '' === alsoHere ? undefined : alsoHere);
205
+ const suffix = clause === undefined ? '' : ` — ${clause}`;
206
+ return {
207
+ pass: false,
208
+ failureReason: `no element matched ${subject}${state === undefined ? '' : ` in state '${state}'`}${suffix}`,
209
+ observed: `no matching element on the page${suffix}`,
210
+ expected: `an element matching ${subject}${state === undefined ? '' : ` in state '${state}'`}`,
211
+ assertion: 'element.present',
212
+ ...(present.length > 0 ? { evidence: { presentTestids: present } } : {}),
213
+ };
214
+ }
@@ -1,4 +1,5 @@
1
1
  import { PredicateKind, type ReticleEvent } from '@reticlehq/core';
2
+ export { evalConsole } from './predicate-console.js';
2
3
  import type { Predicate } from './predicate-schema.js';
3
4
  export * from './predicate-schema.js';
4
5
  export interface EvalResult {
@@ -66,12 +67,23 @@ export declare function str(value: unknown): string | undefined;
66
67
  * `{$gte,$lte,$gt,$lt}` (numbers), `{$contains}` (array membership or substring), `{$length}`.
67
68
  */
68
69
  export declare function matchValue(got: unknown, want: unknown): boolean;
70
+ /** Shallow JSON pattern match: each key in `pattern` must match (see matchValue). */
71
+ export declare function dataMatches(actual: Record<string, unknown>, pattern: Record<string, unknown>): boolean;
72
+ /** Enough of a body to see what differed, without paying for a whole payload in the verdict. */
73
+ export declare function clipBody(body: string): string;
74
+ /**
75
+ * The filter as APPLIED, every field of it.
76
+ *
77
+ * The count failure used to print `{method, urlContains, status}` while `ok` and `bodyContains` were
78
+ * applied too, so the caller was shown a predicate it had not written and told nothing matched it.
79
+ * A printed filter that is narrower than the real one is worse than none: it is believed.
80
+ */
81
+ export declare function describeNetFilter(p: Extract<Predicate, {
82
+ kind: typeof PredicateKind.NET;
83
+ }>): string;
69
84
  export declare function evalNet(events: ReticleEvent[], p: Extract<Predicate, {
70
85
  kind: typeof PredicateKind.NET;
71
86
  }>): EvalResult;
72
- export declare function evalConsole(events: ReticleEvent[], p: Extract<Predicate, {
73
- kind: typeof PredicateKind.CONSOLE;
74
- }>): EvalResult;
75
87
  export declare function evalAnimation(events: ReticleEvent[], p: Extract<Predicate, {
76
88
  kind: typeof PredicateKind.ANIMATION;
77
89
  }>): EvalResult;