@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
@@ -1,7 +1,10 @@
1
- /** What the resolution produced: a ref to act on, or the reason there isn't one. */
1
+ /** What the resolution produced: a ref to act on, a document-key press, or the reason there isn't one. */
2
2
  export type TargetResolution = {
3
3
  readonly kind: 'ref';
4
4
  readonly ref: string;
5
+ } | {
6
+ readonly kind: 'global';
7
+ readonly ref: '';
5
8
  } | {
6
9
  readonly kind: 'error';
7
10
  readonly message: string;
@@ -22,5 +25,8 @@ export type TargetResolution = {
22
25
  * Invisible matches are filtered before counting: a hidden duplicate is a common way for a
23
26
  * legitimately unambiguous query to look ambiguous, and refusing on it would push callers back to
24
27
  * the two-turn path for no gain.
28
+ *
29
+ * When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
30
+ * agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
25
31
  */
26
32
  export declare function resolveTargetRef(candidates: readonly unknown[]): TargetResolution;
@@ -1,4 +1,67 @@
1
+ import { ElementState } from '@reticlehq/core';
1
2
  import { asRecord } from './tools-helpers.js';
3
+ /**
4
+ * Roles that usually name the control an agent meant, ahead of a wrapping `generic` that
5
+ * happened to inherit the same accessible name (header CTA vs empty-state CTA).
6
+ */
7
+ const ROLE_RANK = {
8
+ button: 0,
9
+ link: 1,
10
+ menuitem: 2,
11
+ tab: 3,
12
+ checkbox: 4,
13
+ radio: 5,
14
+ switch: 6,
15
+ option: 7,
16
+ combobox: 8,
17
+ textbox: 9,
18
+ searchbox: 10,
19
+ };
20
+ function roleRank(role) {
21
+ if ('string' !== typeof role)
22
+ return 100;
23
+ return ROLE_RANK[role] ?? 50;
24
+ }
25
+ function statesOf(c) {
26
+ return Array.isArray(c.states) ? c.states.filter((s) => 'string' === typeof s) : [];
27
+ }
28
+ function inViewport(c) {
29
+ return statesOf(c).includes(ElementState.IN_VIEWPORT);
30
+ }
31
+ /**
32
+ * Stable ranking for the refusal message only. Never used to pick — an action must not guess.
33
+ *
34
+ * Order: in-viewport over off-screen, then more-specific roles over `generic`. Visibility is
35
+ * already applied before this runs (hidden duplicates are filtered), so it is not a sort key here.
36
+ */
37
+ function compareCandidates(a, b) {
38
+ const viewport = Number(inViewport(b)) - Number(inViewport(a));
39
+ if (0 !== viewport)
40
+ return viewport;
41
+ return roleRank(a.role) - roleRank(b.role);
42
+ }
43
+ /**
44
+ * Annotate where the candidate sits. `anyInViewport` is true when at least one sibling carried
45
+ * `inViewport` — only then is absence meaningful as off-screen. Without that signal, saying
46
+ * "off-screen" would invent a fact the descriptor never reported.
47
+ */
48
+ function formatCandidate(c, anyInViewport) {
49
+ const role = 'string' === typeof c.role ? c.role : '?';
50
+ const name = 'string' === typeof c.name ? c.name : '';
51
+ const ref = 'string' === typeof c.ref ? c.ref : '?';
52
+ const visible = false !== c.visible;
53
+ let where;
54
+ if (!visible)
55
+ where = 'hidden';
56
+ else if (inViewport(c))
57
+ where = 'in-viewport';
58
+ else if (anyInViewport)
59
+ where = 'off-screen';
60
+ else
61
+ where = 'visible';
62
+ const head = name.length > 0 ? `${ref} (${role} "${name}"` : `${ref} (${role}`;
63
+ return `${head}; ${where})`;
64
+ }
2
65
  /**
3
66
  * Turn a target QUERY into the single ref an action can be performed on.
4
67
  *
@@ -15,6 +78,9 @@ import { asRecord } from './tools-helpers.js';
15
78
  * Invisible matches are filtered before counting: a hidden duplicate is a common way for a
16
79
  * legitimately unambiguous query to look ambiguous, and refusing on it would push callers back to
17
80
  * the two-turn path for no gain.
81
+ *
82
+ * When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
83
+ * agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
18
84
  */
19
85
  export function resolveTargetRef(candidates) {
20
86
  const all = candidates.map((c) => asRecord(c));
@@ -28,20 +94,17 @@ export function resolveTargetRef(candidates) {
28
94
  };
29
95
  }
30
96
  if (usable.length > 1) {
31
- const named = usable
97
+ const ranked = [...usable].sort(compareCandidates);
98
+ const anyInViewport = ranked.some(inViewport);
99
+ const named = ranked
32
100
  .slice(0, 5)
33
- .map((c) => {
34
- const role = 'string' === typeof c.role ? c.role : '?';
35
- const name = 'string' === typeof c.name ? c.name : '';
36
- const ref = 'string' === typeof c.ref ? c.ref : '?';
37
- return name.length > 0 ? `${ref} (${role} "${name}")` : `${ref} (${role})`;
38
- })
101
+ .map((c) => formatCandidate(c, anyInViewport))
39
102
  .join(', ');
40
103
  return {
41
104
  kind: 'error',
42
105
  message: `target matched ${String(usable.length)} elements and an action must not guess between ` +
43
- `them: ${named}. Narrow the query (add role/name/testid or a scope), or pass an explicit ` +
44
- '`ref` from reticle_query.',
106
+ `them (ranked: in-viewport, then role): ${named}. Narrow the query (add role/name/testid ` +
107
+ 'or a scope), or pass an explicit `ref` from reticle_query.',
45
108
  };
46
109
  }
47
110
  const only = usable[0];
@@ -77,7 +77,7 @@ export declare function repeatedRequestLabels(events: readonly {
77
77
  type: string;
78
78
  data: Record<string, unknown>;
79
79
  }[]): string[];
80
- export interface SettleResult {
80
+ interface SettleResult {
81
81
  /** True when nothing this window started is still outstanding. */
82
82
  settled: boolean;
83
83
  /** What was still open when the wait ended — empty when `settled`. */
@@ -27,7 +27,7 @@
27
27
  * - still in flight when the budget genuinely runs out → `unsettled`, as before, but now an honest
28
28
  * limit instead of an early exit.
29
29
  */
30
- import { EventType, isDevToolingUrl } from '@reticlehq/core';
30
+ import { EventType, NetInitiator, isDevToolingUrl } from '@reticlehq/core';
31
31
  /**
32
32
  * The dev toolchain talking about ITSELF is not the app finishing its work.
33
33
  *
@@ -37,6 +37,13 @@ import { EventType, isDevToolingUrl } from '@reticlehq/core';
37
37
  * was graded on the strength of a Next webpack hot-update the same payload printed as ignored.
38
38
  */
39
39
  const isDevTooling = (data) => isDevToolingUrl('string' === typeof data['url'] ? data['url'] : undefined);
40
+ /**
41
+ * A departure records where the browser was SENT, not a request whose result we will see — the
42
+ * response is delivered to a document this session does not live into. It can never be matched by a
43
+ * NET_REQUEST, so counting it as outstanding would mean no page containing an outbound link or a
44
+ * download button ever settles again. See `NetInitiator.NAVIGATION`.
45
+ */
46
+ const isDeparture = (data) => data['initiator'] === NetInitiator.NAVIGATION;
40
47
  /** How often to re-read the buffer. Short enough to return promptly, long enough not to spin. */
41
48
  const POLL_MS = 50;
42
49
  const idOf = (data) => 'string' === typeof data['id'] ? data['id'] : undefined;
@@ -58,7 +65,7 @@ export function inFlightRequestIds(events) {
58
65
  }
59
66
  const open = [];
60
67
  for (const e of events) {
61
- if (e.type !== EventType.NET_PENDING || isDevTooling(e.data))
68
+ if (e.type !== EventType.NET_PENDING || isDevTooling(e.data) || isDeparture(e.data))
62
69
  continue;
63
70
  const id = idOf(e.data);
64
71
  if (id !== undefined && !settled.has(id) && !open.includes(id))
@@ -12,6 +12,17 @@
12
12
  * invalidates the prior snapshot (a diff across pages would be meaningless), so the next snapshot
13
13
  * returns full — never a misleading cross-page delta.
14
14
  */
15
+ /**
16
+ * Pure: was the prior snapshot of this scope both RECENT and SMALLER than this one? If so, the
17
+ * earlier reading was very likely taken mid-load — content arrived in the gap between the two calls
18
+ * — and any presence-grade conclusion drawn from that earlier snapshot ("this route renders
19
+ * nothing") is suspect. Silent otherwise: a stale prior or a page that shrank (or stayed the same
20
+ * size) is not this trap.
21
+ */
22
+ export declare function snapshotGrowthNote(prior: {
23
+ tree: string;
24
+ atMs: number;
25
+ } | undefined, nextTree: string, nowMs: number, windowMs?: number): string | undefined;
15
26
  export declare const SnapshotDeltaMode: {
16
27
  readonly FULL: "full";
17
28
  readonly DELTA: "delta";
@@ -45,11 +56,23 @@ export declare function snapshotDelta(prevTree: string | undefined, nextTree: st
45
56
  /** Per-(session,scope,mode) last-snapshot cache. Route-aware: a route change invalidates the entry. */
46
57
  export declare class SnapshotCache {
47
58
  #private;
48
- constructor(max?: number);
59
+ constructor(max?: number, now?: () => number);
49
60
  /** True when an entry exists for this key (regardless of route match). */
50
61
  has(key: string): boolean;
62
+ /** The clock this cache was constructed with — callers compare against it so `remember`'s
63
+ * timestamp and a caller's "now" can never drift onto two different clocks. */
64
+ now(): number;
51
65
  /** Last tree for this key IF the route still matches; undefined when absent or route changed. */
52
66
  recall(key: string, route: string): string | undefined;
67
+ /**
68
+ * The last remembered tree for this key AND when it was remembered, IF the route still matches.
69
+ * Same route-gating as `recall` — a route change makes the prior entry meaningless for comparison,
70
+ * not just for diffing.
71
+ */
72
+ priorEntry(key: string, route: string): {
73
+ tree: string;
74
+ atMs: number;
75
+ } | undefined;
53
76
  remember(key: string, route: string, tree: string): void;
54
77
  }
55
78
  export declare function snapshotCacheKey(sessionId: string, scope: string, mode: string): string;
@@ -158,6 +158,37 @@ function focusedLine(tree) {
158
158
  const text = withoutFocus(focused).trim();
159
159
  return { id: REF_MARKER.exec(focused)?.[0]?.trim() ?? deltaKey(focused), text };
160
160
  }
161
+ /**
162
+ * How recent a smaller prior snapshot has to be for its growth to be worth flagging (#787) — long
163
+ * enough to catch the "route committed, then hydrated" window the bug report describes (hundreds of
164
+ * ms up to a couple of seconds), short enough that an unrelated LATER snapshot of a page that simply
165
+ * grew through normal use (an infinite-scroll load, a live feed) doesn't get blamed on load timing.
166
+ */
167
+ const GROWTH_NOTE_WINDOW_MS = 5000;
168
+ /** Non-empty lines only — a blank line in the tree text is formatting, not a node. */
169
+ function nodeCount(tree) {
170
+ return tree.split('\n').filter((line) => '' !== line.trim()).length;
171
+ }
172
+ /**
173
+ * Pure: was the prior snapshot of this scope both RECENT and SMALLER than this one? If so, the
174
+ * earlier reading was very likely taken mid-load — content arrived in the gap between the two calls
175
+ * — and any presence-grade conclusion drawn from that earlier snapshot ("this route renders
176
+ * nothing") is suspect. Silent otherwise: a stale prior or a page that shrank (or stayed the same
177
+ * size) is not this trap.
178
+ */
179
+ export function snapshotGrowthNote(prior, nextTree, nowMs, windowMs = GROWTH_NOTE_WINDOW_MS) {
180
+ if (prior === undefined)
181
+ return undefined;
182
+ const elapsedMs = nowMs - prior.atMs;
183
+ if (elapsedMs < 0 || elapsedMs > windowMs)
184
+ return undefined;
185
+ const priorCount = nodeCount(prior.tree);
186
+ const nextCount = nodeCount(nextTree);
187
+ if (nextCount <= priorCount)
188
+ return undefined;
189
+ return (`this scope had ${String(priorCount)} nodes ${String(elapsedMs)}ms ago and has ${String(nextCount)} now — ` +
190
+ 'content likely arrived after your last look; a presence conclusion drawn from that earlier snapshot may have been taken mid-load');
191
+ }
161
192
  export const SnapshotDeltaMode = {
162
193
  FULL: 'full',
163
194
  DELTA: 'delta',
@@ -200,15 +231,30 @@ const DEFAULT_MAX_ENTRIES = 50;
200
231
  export class SnapshotCache {
201
232
  #map = new Map();
202
233
  #max;
203
- constructor(max = DEFAULT_MAX_ENTRIES) {
234
+ #now;
235
+ constructor(max = DEFAULT_MAX_ENTRIES, now = Date.now) {
204
236
  this.#max = max;
237
+ this.#now = now;
205
238
  }
206
239
  /** True when an entry exists for this key (regardless of route match). */
207
240
  has(key) {
208
241
  return this.#map.has(key);
209
242
  }
243
+ /** The clock this cache was constructed with — callers compare against it so `remember`'s
244
+ * timestamp and a caller's "now" can never drift onto two different clocks. */
245
+ now() {
246
+ return this.#now();
247
+ }
210
248
  /** Last tree for this key IF the route still matches; undefined when absent or route changed. */
211
249
  recall(key, route) {
250
+ return this.priorEntry(key, route)?.tree;
251
+ }
252
+ /**
253
+ * The last remembered tree for this key AND when it was remembered, IF the route still matches.
254
+ * Same route-gating as `recall` — a route change makes the prior entry meaningless for comparison,
255
+ * not just for diffing.
256
+ */
257
+ priorEntry(key, route) {
212
258
  const entry = this.#map.get(key);
213
259
  if (entry === undefined || entry.route !== route)
214
260
  return undefined;
@@ -216,7 +262,7 @@ export class SnapshotCache {
216
262
  // (Map preserves insertion order; delete+set moves it to the most-recently-used end).
217
263
  this.#map.delete(key);
218
264
  this.#map.set(key, entry);
219
- return entry.tree;
265
+ return { tree: entry.tree, atMs: entry.atMs };
220
266
  }
221
267
  remember(key, route, tree) {
222
268
  // Re-insert to move the key to the most-recently-used end (a plain set keeps its old position).
@@ -226,7 +272,7 @@ export class SnapshotCache {
226
272
  if (oldest !== undefined)
227
273
  this.#map.delete(oldest);
228
274
  }
229
- this.#map.set(key, { route, tree });
275
+ this.#map.set(key, { route, tree, atMs: this.#now() });
230
276
  }
231
277
  }
232
278
  /**
@@ -260,17 +306,23 @@ export function applySnapshotDelta(raw, opts, cache) {
260
306
  : {};
261
307
  const route = 'string' === typeof status['route'] ? status['route'] : '';
262
308
  const key = snapshotCacheKey(opts.sessionId, opts.scope, opts.mode);
309
+ // Read the growth signal BEFORE remember() overwrites the entry it compares against — needed on
310
+ // both branches below (#787), not only the diff:true path, since the reporter's own repro used a
311
+ // plain snapshot the second time.
312
+ const priorEntry = cache.priorEntry(key, route);
313
+ const growthNote = snapshotGrowthNote(priorEntry, tree, cache.now());
314
+ const growth = growthNote === undefined ? {} : { growthWarning: growthNote };
263
315
  if (!opts.diff) {
264
316
  cache.remember(key, route, tree);
265
- return raw;
317
+ return { ...r, ...growth };
266
318
  }
267
- const prev = cache.recall(key, route);
319
+ const prev = priorEntry?.tree;
268
320
  const hadEntry = cache.has(key);
269
321
  cache.remember(key, route, tree);
270
322
  const decision = snapshotDelta(prev, tree);
271
323
  if (decision.mode === SnapshotDeltaMode.FULL) {
272
324
  const reason = hadEntry ? 'route changed' : 'first snapshot for this route';
273
- return { ...r, mode: SnapshotDeltaMode.FULL, reason };
325
+ return { ...r, mode: SnapshotDeltaMode.FULL, reason, ...growth };
274
326
  }
275
327
  // The walk stops at a node cap and returns a DOCUMENT-ORDER PREFIX, so two capped snapshots of a
276
328
  // large page are identical whenever the change happened past the cap — and "unchanged" is then a
@@ -282,7 +334,13 @@ export function applySnapshotDelta(raw, opts, cache) {
282
334
  // field is simply absent forever.
283
335
  const moved = decision.focusChanged === undefined ? {} : { focusChanged: decision.focusChanged };
284
336
  if (decision.mode === SnapshotDeltaMode.UNCHANGED) {
285
- return { mode: SnapshotDeltaMode.UNCHANGED, status: r['status'], ...moved, ...capped };
337
+ return {
338
+ mode: SnapshotDeltaMode.UNCHANGED,
339
+ status: r['status'],
340
+ ...moved,
341
+ ...capped,
342
+ ...growth,
343
+ };
286
344
  }
287
345
  const { changed, ...structuralDelta } = decision.delta;
288
346
  const changedField = changed.length > 0 ? { changed, changedCount: changed.length } : {};
@@ -293,5 +351,6 @@ export function applySnapshotDelta(raw, opts, cache) {
293
351
  status: r['status'],
294
352
  ...moved,
295
353
  ...capped,
354
+ ...growth,
296
355
  };
297
356
  }
@@ -10,6 +10,7 @@ export declare const ReticleTool: {
10
10
  readonly ACT_SEQUENCE: "reticle_act_sequence";
11
11
  readonly ACT_AND_WAIT: "reticle_act_and_wait";
12
12
  readonly OBSERVE: "reticle_observe";
13
+ readonly LINEAGE: "reticle_lineage";
13
14
  readonly WAIT_FOR: "reticle_wait_for";
14
15
  readonly NETWORK: "reticle_network";
15
16
  readonly CONSOLE: "reticle_console";
@@ -11,6 +11,7 @@ export const ReticleTool = {
11
11
  ACT_SEQUENCE: FlowStepTool.ACT_SEQUENCE,
12
12
  ACT_AND_WAIT: FlowStepTool.ACT_AND_WAIT,
13
13
  OBSERVE: 'reticle_observe',
14
+ LINEAGE: 'reticle_lineage',
14
15
  WAIT_FOR: 'reticle_wait_for',
15
16
  NETWORK: 'reticle_network',
16
17
  CONSOLE: 'reticle_console',
@@ -215,6 +215,26 @@ export const CORE_TOOL_NAMES = new Set([
215
215
  // deleting the instruction from the lease and the pause hint — deletes the handback protocol,
216
216
  // because there is nowhere else those two calls are ever named.
217
217
  ReticleTool.SESSION,
218
+ // VERIFY is the only advertised tool that can CONCLUDE, and its absence was measurably the most
219
+ // expensive thing on this surface.
220
+ //
221
+ // Measured on the Layer B agent loop (bench/raw/agent-loop-results.json, Haiku 4.5, 25-turn cap):
222
+ // on the no-regression control — a CLEAN app, where the right answer is "nothing is wrong" —
223
+ // Playwright MCP concluded in 7 turns and Reticle burned all 25 and never answered, at 333k tokens
224
+ // against Playwright's 54k. That one cell was a quarter of Reticle's total cost across five
225
+ // scenarios. Removing it, Reticle is at parity on the other four and wins two outright.
226
+ //
227
+ // The cause is compositional rather than behavioural: every other tool in this set answers "here
228
+ // is more to look at". None of them can say "there is nothing more". On a healthy app — which is
229
+ // most runs — that is a loop with no exit, and 25 was just where the cap fell.
230
+ //
231
+ // `reticle_verify` is the exit. { action: "crawl" } drives every reachable control itself and
232
+ // reports the whole fault set in ONE call, and { action: "flows" } replays the saved flows with no
233
+ // model in the loop at all — the path Layer C measures at 271 tokens, deterministic, 0% flake.
234
+ // Both were built and neither was reachable: the same "a tool an agent must already know about is
235
+ // a tool that never gets called" argument that put INSPECT and FEEDBACK here, applied to the one
236
+ // tool that ends the loop.
237
+ ReticleTool.VERIFY,
218
238
  ]);
219
239
  /**
220
240
  * The extended surface: what `all` advertises BEYOND the default set.
@@ -269,8 +289,6 @@ export const EXTENDED_TOOL_NAMES = new Set([
269
289
  ReticleTool.CLOCK,
270
290
  ReticleTool.NETWORK_MOCK,
271
291
  ReticleTool.STORAGE,
272
- // Merged change/flows/affected/coverage/crawl — one name where three of these used to sit.
273
- ReticleTool.VERIFY,
274
292
  ]);
275
293
  /**
276
294
  * The verify surface: one acting tool that returns a verdict, plus the two meta-tools that reach
@@ -17,6 +17,8 @@ import { paginateQueryResult } from './query-paginate.js';
17
17
  */
18
18
  const QUERY_BY_VALUES = Object.values(QueryBy);
19
19
  const QUERY_BY_LIST = QUERY_BY_VALUES.join(' | ');
20
+ /** Every `by`/`value` shorthand says the same thing; the parameter name carries the rest. */
21
+ const QUERY_ALIAS = 'Predicate spelling of by/value.';
20
22
  const queryByEnum = z.enum(QUERY_BY_VALUES);
21
23
  import { CONTRACT_TOOLS } from './contract-tools.js';
22
24
  import { DOMAIN_TOOLS } from '../domain/domain-tools.js';
@@ -42,6 +44,7 @@ import { sessionIdShape, commandOrThrow } from './tool-kit.js';
42
44
  import { applyMerges } from './merge-tools.js';
43
45
  import { ACT_TOOLS } from './act-tools.js';
44
46
  import { OBSERVE_TOOLS } from './observe-tools.js';
47
+ import { LINEAGE_TOOLS } from './lineage-tools.js';
45
48
  import { RECONCILE_TOOLS } from './reconcile-tools.js';
46
49
  import { READ_TOOLS } from './read-tools.js';
47
50
  import { LEASE_TOOLS } from './lease-tools.js';
@@ -117,6 +120,13 @@ export const RAW_TOOLS = [
117
120
  })
118
121
  .optional()
119
122
  .describe("Present ONLY when `sessions` is empty: the same answer as `why`, executable. `command` is the LITERAL command to run, sourced from this project's own package.json scripts and lockfile — it is absent, never guessed, when the project declares no dev script. `action` is one of daemon_split | start_dev_server | run_init | open_app | reopen_app. `daemon_split` outranks the rest and means the app IS running and instrumented, on a DIFFERENT daemon than the one you are attached to — do not start or re-init anything, read `reason`."),
123
+ lastKnown: z
124
+ .object({
125
+ sessionId: z.string(),
126
+ url: z.string(),
127
+ })
128
+ .optional()
129
+ .describe('Present ONLY when `sessions` is empty AND a tab was connected to this daemon earlier: the last sessionId and URL that tab was on when it disappeared. A route that 500s tears the page down and the SDK never reconnects — this is the last thing we knew, not a route-500 verdict. An empty list with no lastKnown is a tab that never arrived.'),
120
130
  },
121
131
  handler: async (deps) => {
122
132
  const provider = deps.realInput;
@@ -135,10 +145,14 @@ export const RAW_TOOLS = [
135
145
  // The executable half. `why` is for the human reading the transcript; this is the one the
136
146
  // agent acts on, so it never has to parse a paragraph to find a command inside it.
137
147
  const next = deps.sessions.noSessionNextAction();
148
+ // The last tab, when one was here. Optional-call: test stubs of SessionManager predate it.
149
+ const known = deps.sessions.lastKnown?.();
150
+ const lastKnown = undefined === known ? undefined : { sessionId: known.id, url: known.url };
138
151
  return {
139
152
  sessions,
140
153
  ...(why === undefined ? {} : { why }),
141
154
  ...(next === undefined ? {} : { next_action: next }),
155
+ ...(undefined === lastKnown ? {} : { lastKnown }),
142
156
  };
143
157
  }
144
158
  return { sessions };
@@ -214,11 +228,15 @@ export const RAW_TOOLS = [
214
228
  note: z
215
229
  .string()
216
230
  .optional()
217
- .describe('Present when a diff was computed over a capped tree — "unchanged" is then partial.'),
231
+ .describe('Why this result is not what it looks like. Four causes, all of which otherwise read as "the page is empty": a diff computed over a capped tree ("unchanged" is then partial); an interactive-mode tree emptied by leanness; a page whose elements all computed hidden; and an UNMOUNTED app, where the DOM under the scope holds almost nothing — that last one will not improve by waiting, so read reticle_console for an uncaught error instead.'),
218
232
  scopeMissing: z
219
233
  .boolean()
220
234
  .optional()
221
235
  .describe('True when a scope was given but resolved to nothing — the tree is EMPTY on purpose, not because the page is empty. Do not read an absent element as absent from the page; re-check the scope.'),
236
+ growthWarning: z
237
+ .string()
238
+ .optional()
239
+ .describe('Present when this same scope was smaller moments ago — content likely arrived after your last look, and a presence/absence conclusion drawn from that earlier snapshot may have been taken mid-load. Re-check rather than trusting the earlier read.'),
222
240
  },
223
241
  // async so a synchronous resolve() failure (no session connected) surfaces as a REJECTED promise —
224
242
  // the handler contract every caller relies on — not a throw that escapes a direct invocation.
@@ -233,24 +251,24 @@ export const RAW_TOOLS = [
233
251
  return commandOrThrow(deps, resolved.id, ReticleCommand.SNAPSHOT, {
234
252
  scope: args['scope'],
235
253
  mode,
236
- }).then((raw) => withSizeCost(noteEmptyLeanTree(applySnapshotDelta(raw, {
254
+ }).then((raw) => withSizeCost(noteUnmountedRoot(noteHiddenPage(noteEmptyLeanTree(applySnapshotDelta(raw, {
237
255
  sessionId: resolved.id,
238
256
  scope: asString(args['scope']) ?? '',
239
257
  mode,
240
258
  diff: true === args['diff'],
241
- }, SNAPSHOT_CACHE), mode)));
259
+ }, SNAPSHOT_CACHE), mode), mode), mode)));
242
260
  },
243
261
  },
244
262
  {
245
263
  name: ReticleTool.QUERY,
246
264
  example: { by: 'testid', value: 'todo-list' },
247
- description: `Find elements by Testing-Library semantics, INCLUDING open shadow roots — \`count_only:true\` gives just the count (~30x smaller); \`limit\` caps descriptors. Pass \`by\` (${QUERY_BY_LIST}) and \`value\` (the query string). Returns matching refs + descriptors + visibility. Pass \`attrs:["href"]\` to project attributes (link/image URLs) onto each match. Pass \`limit\` to cap descriptors (broad role queries can be large) or \`count_only:true\` for just the match count — both cut tokens. On zero matches, also returns hint:{ route, presentRegions[], knownEmptyState } so you can distinguish an empty state from a missing element WITHOUT taking a snapshot. ASKING SEVERAL QUESTIONS ABOUT THE PAGE? One reticle_snapshot answers them all at once — a run of queries costs a round trip each and returns what the snapshot already held.`,
265
+ description: `Find elements by Testing-Library semantics, INCLUDING open shadow roots — \`count_only:true\` gives just the count (~30x smaller); \`limit\` caps descriptors. Pass \`by\` (${QUERY_BY_LIST}) and \`value\` (the query string). Returns matching refs + descriptors + visibility. Pass \`attrs:["href"]\` to project attributes (link/image URLs) onto each match. Pass \`limit\` to cap descriptors (broad role queries can be large) or \`count_only:true\` for just the match count — both cut tokens. On zero matches, also returns hint:{ route, presentRegions[], knownEmptyState, nameNearMiss[] } so you can distinguish an empty state from a missing element WITHOUT taking a snapshot — nameNearMiss carries the labels that role really has when an exact role+name query just missed. ASKING SEVERAL QUESTIONS ABOUT THE PAGE? One reticle_snapshot answers them all at once — a run of queries costs a round trip each and returns what the snapshot already held.`,
248
266
  inputSchema: {
249
267
  // Constrained to the enum, NOT z.string(). A free string let `by:'css'` through to the
250
268
  // browser's `default: return []`, so an unsupported strategy answered "0 matches" — which
251
269
  // reads as "the element is not on the page". Measured on a live page: by:'css' value:'body'
252
270
  // returned count 0. A false negative is the one answer this product must never invent.
253
- by: queryByEnum.optional().describe(`Query strategy: ${QUERY_BY_LIST}`),
271
+ by: queryByEnum.optional().describe('Query strategy.'),
254
272
  value: z
255
273
  .string()
256
274
  .optional()
@@ -280,12 +298,15 @@ export const RAW_TOOLS = [
280
298
  .describe('Return just { count } (no element descriptors) — use when you only need "how many match?" and not their refs.'),
281
299
  // The predicate's spelling, accepted as an alias for by/value so the shape an agent learns from
282
300
  // act_and_wait/assert also works here. See query-shape.ts.
283
- testid: z.string().optional().describe('Alias for by="testid" value=… (predicate spelling).'),
284
- text: z.string().optional().describe('Alias for by="text" value=… (predicate spelling).'),
285
- role: z.string().optional().describe('Alias for by="role" value=… (predicate spelling).'),
286
- label: z.string().optional().describe('Alias for by="label" value=… (predicate spelling).'),
287
- placeholder: z.string().optional().describe('Alias for by="placeholder" value=… .'),
288
- alt: z.string().optional().describe('Alias for by="alt" value=… (predicate spelling).'),
301
+ // Six copies of one sentence, re-sent every turn. The parameter NAME already says which
302
+ // strategy it selects, and they sit in a block together, so each only has to say it is the
303
+ // predicate spelling of by/value.
304
+ testid: z.string().optional().describe(QUERY_ALIAS),
305
+ text: z.string().optional().describe(QUERY_ALIAS),
306
+ role: z.string().optional().describe(QUERY_ALIAS),
307
+ label: z.string().optional().describe(QUERY_ALIAS),
308
+ placeholder: z.string().optional().describe(QUERY_ALIAS),
309
+ alt: z.string().optional().describe(QUERY_ALIAS),
289
310
  ...sessionIdShape,
290
311
  },
291
312
  outputSchema: {
@@ -355,6 +376,12 @@ export const RAW_TOOLS = [
355
376
  .passthrough()
356
377
  .optional()
357
378
  .describe("Present when a TEXT search missed but the string IS on the page, split across this container's children. Retry as { scope: <ref>, self: true } — no text query can match a string no single element owns."),
379
+ // Declared here for the same reason presentRegions had to be: the browser emits it, and an
380
+ // undeclared field is stripped from structuredContent without a word.
381
+ nameNearMiss: z
382
+ .array(z.string())
383
+ .optional()
384
+ .describe('Present when a ROLE+NAME search missed and that role DOES carry a nearly-matching name — role+name is exact, so "Mesh" does not find "2 Mesh". Retry with one of these spellings; no snapshot needed.'),
358
385
  })
359
386
  .optional()
360
387
  .describe('Present only on zero matches — tells you what IS on the page so you can diagnose the miss.'),
@@ -516,6 +543,7 @@ export const RAW_TOOLS = [
516
543
  ...BROWSER_TOOLS,
517
544
  ...ACT_TOOLS,
518
545
  ...OBSERVE_TOOLS,
546
+ ...LINEAGE_TOOLS,
519
547
  ...READ_TOOLS,
520
548
  ...LEASE_TOOLS,
521
549
  // reticle_feedback — the agent reports that RETICLE failed (not that the app did). See feedback-tools.ts.
@@ -627,4 +655,85 @@ function noteEmptyLeanTree(result, mode) {
627
655
  `by testid rather than by role.`,
628
656
  };
629
657
  }
658
+ /**
659
+ * An empty tree on a page full of elements is the same claim, from the other cause.
660
+ *
661
+ * `noteEmptyLeanTree` covers leanness and returns early for every other mode, so a `full` snapshot
662
+ * that comes back `{ tree: "", nodes: 0 }` says nothing at all — and that is the shape #672 was
663
+ * reported as: 44 buttons and 12 textboxes on the page, every one computing hidden, both modes
664
+ * empty. The reporter spent about six tool calls and a large console dump establishing the page was
665
+ * fine and the snapshot was wrong, then drove the flow off `reticle_query` refs, which is a
666
+ * workaround no tool description mentions.
667
+ *
668
+ * So the note names the count, says plainly that this is not an empty page, and hands over the path
669
+ * that does work. It does NOT diagnose why the page is hidden: the walk knows what it skipped and
670
+ * cannot know why, and a note that guessed would be the same kind of overconfident answer as the
671
+ * empty tree it replaces.
672
+ */
673
+ /**
674
+ * How much DOM there is under the mount container, when the tree is empty and nothing was skipped.
675
+ *
676
+ * A handful of elements is a container with nothing in it. React's own root div counts as one, and a
677
+ * wrapper or two is ordinary, so this is deliberately not `=== 0`.
678
+ */
679
+ const UNMOUNTED_ROOT_MAX_ELEMENTS = 3;
680
+ /**
681
+ * The third cause of an empty tree, and the one the walk cannot see: the app is not mounted.
682
+ *
683
+ * Reported from the field with the react-three-fiber crash. The source-mapping stamp threw inside
684
+ * R3F's commit phase, React unmounted the entire tree, and the page went white — and the snapshot
685
+ * answered `{ tree: "", nodes: 0 }`, which is also what a page that has not rendered yet answers.
686
+ * The reporter spent a diagnosis pass separating the two, and said that detecting a dead root would
687
+ * have pointed straight at the cause.
688
+ *
689
+ * The other two notes here explain an empty tree by what the WALK passed over. This one cannot: a
690
+ * walk that visited nothing has nothing to have skipped. It reads the DOM count instead, which is
691
+ * why the browser now reports it, and it fires only when the skip counts are silent — a page whose
692
+ * elements were all hidden has plenty of DOM and belongs to `noteHiddenPage`.
693
+ *
694
+ * Like both of its neighbours it names a fact and hands over the next read, and does not diagnose:
695
+ * "not mounted" is certain from the count, WHY is not, and the console is where the answer is.
696
+ */
697
+ function noteUnmountedRoot(result, mode) {
698
+ if (SnapshotMode.STATUS === mode)
699
+ return result;
700
+ const row = asRecord(result);
701
+ if (0 !== asNumber(row['nodes']))
702
+ return result;
703
+ if (row['note'] !== undefined)
704
+ return result;
705
+ const elements = asNumber(row['domElements']);
706
+ if (elements === undefined || elements > UNMOUNTED_ROOT_MAX_ELEMENTS)
707
+ return result;
708
+ return {
709
+ ...row,
710
+ note: `the tree is empty because there is almost nothing in the DOM: ${String(elements)} ` +
711
+ `element(s) under this scope. That is a mount container with nothing rendered into it, not a ` +
712
+ `page whose contents were skipped — so the app is UNMOUNTED rather than slow, and waiting ` +
713
+ `will not change it. An app that was rendering and then stopped has usually thrown: read ` +
714
+ `reticle_console for an uncaught error, and check anything that runs inside a framework ` +
715
+ `commit phase. If the app has genuinely not started yet, load it and snapshot again.`,
716
+ };
717
+ }
718
+ function noteHiddenPage(result, mode) {
719
+ if (SnapshotMode.STATUS === mode)
720
+ return result;
721
+ const row = asRecord(result);
722
+ if (0 !== asNumber(row['nodes']))
723
+ return result;
724
+ // A note already there is the more specific one (leanness), and two explanations for one empty
725
+ // tree is worse than the better of them alone.
726
+ if (row['note'] !== undefined)
727
+ return result;
728
+ const skipped = asNumber(row['hiddenSkipped']) ?? 0;
729
+ if (0 === skipped)
730
+ return result;
731
+ return {
732
+ ...row,
733
+ note: `this is NOT an empty page: ${String(skipped)} subtree(s) were skipped because their root ` +
734
+ `computed hidden (aria-hidden, the hidden attribute, or display:none), which is what left the ` +
735
+ `tree empty. If the page is plainly rendered, the visibility computation is the thing that is ` +
736
+ `wrong rather than the app — drive it by reticle_query refs, which do not depend on this walk.`,
737
+ };
738
+ }
630
739
  export const TOOLS = applyMerges(RAW_TOOLS, MERGE_PLANS, RETIRED_FROM_SURFACE);
@@ -29,7 +29,7 @@
29
29
  export declare const SUPPRESS_VERIFY_NEXT_ENV = "RETICLE_SUPPRESS_VERIFY_NEXT";
30
30
  /** What the agent must supply itself. Phrased as an instruction so it cannot be sent verbatim. */
31
31
  export declare const UNTIL_PLACEHOLDER = "<name the consequence this action causes>";
32
- export interface VerifyNextBaton {
32
+ interface VerifyNextBaton {
33
33
  /** The fact that produced this, in the agent's own terms. */
34
34
  why: string;
35
35
  /** A call, ready to make once `until` is filled in. */
@@ -51,3 +51,4 @@ export declare function verifyNextBaton(prose: string, lastAct: {
51
51
  action?: string | undefined;
52
52
  ref?: string | undefined;
53
53
  }): VerifyNextBaton;
54
+ export {};
@@ -11,7 +11,7 @@
11
11
  * stale registry cache and reinstall the skew being fixed. Measured once as pnpm taking 2.2.1 while
12
12
  * npm took 2.3.0 in the next project over.
13
13
  */
14
- import { type PackageManager } from '../init/detect.js';
14
+ import { type PackageManager } from '@reticlehq/init';
15
15
  export { reticleDepsOf } from './reticle-deps.js';
16
16
  /** The install to run, or null when this project has nothing of ours to sync. */
17
17
  export declare function sdkSyncCommand(pm: PackageManager, packages: readonly string[], version: string): {