@reticlehq/server 3.6.0 → 3.7.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 (614) hide show
  1. package/SKILL.md +122 -123
  2. package/dist/command/cli/adhoc-suite.d.ts +14 -0
  3. package/dist/command/cli/adhoc-suite.js +42 -7
  4. package/dist/command/cli/adhoc-verdict.d.ts +18 -2
  5. package/dist/command/cli/adhoc-verdict.js +70 -6
  6. package/dist/command/cli/answers/gate-exit.d.ts +23 -0
  7. package/dist/command/cli/answers/gate-exit.js +45 -0
  8. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  9. package/dist/command/cli/answers/gate-hook-message.js +3 -1
  10. package/dist/command/cli/auth/device-flow.d.ts +13 -2
  11. package/dist/command/cli/auth/device-flow.js +17 -0
  12. package/dist/command/cli/cli-doctor.js +14 -0
  13. package/dist/command/cli/cli-flow-commands.d.ts +0 -11
  14. package/dist/command/cli/cli-flow-commands.js +28 -105
  15. package/dist/command/cli/cli-kill.js +3 -2
  16. package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
  17. package/dist/command/cli/cli-parse-grammar.js +75 -0
  18. package/dist/command/cli/cli-parse-verify.d.ts +2 -0
  19. package/dist/command/cli/cli-parse-verify.js +18 -0
  20. package/dist/command/cli/cli-parse.d.ts +19 -28
  21. package/dist/command/cli/cli-parse.js +82 -182
  22. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  23. package/dist/command/cli/cli-update-commands.js +61 -9
  24. package/dist/command/cli/cli-usage.d.ts +33 -0
  25. package/dist/command/cli/cli-usage.js +287 -0
  26. package/dist/command/cli/cli-verify.d.ts +34 -4
  27. package/dist/command/cli/cli-verify.js +81 -12
  28. package/dist/command/cli/cloud-cli.d.ts +13 -0
  29. package/dist/command/cli/cloud-cli.js +145 -333
  30. package/dist/command/cli/cloud-kit.d.ts +12 -0
  31. package/dist/command/cli/cloud-kit.js +41 -2
  32. package/dist/command/cli/cloud-link.d.ts +57 -0
  33. package/dist/command/cli/cloud-link.js +308 -0
  34. package/dist/command/cli/cloud-login.d.ts +15 -1
  35. package/dist/command/cli/cloud-login.js +67 -13
  36. package/dist/command/cli/context-command.d.ts +1 -0
  37. package/dist/command/cli/context-command.js +33 -0
  38. package/dist/command/cli/daemon-start-options.d.ts +3 -0
  39. package/dist/command/cli/daemon-start-options.js +24 -3
  40. package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
  41. package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
  42. package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
  43. package/dist/command/cli/doctor/doctor-project-line.js +5 -4
  44. package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
  45. package/dist/command/cli/doctor/doctor-rows.js +6 -0
  46. package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
  47. package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
  48. package/dist/command/cli/known-regression.d.ts +13 -0
  49. package/dist/command/cli/known-regression.js +34 -0
  50. package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
  51. package/dist/command/cli/launch/sdk-version-match.js +25 -0
  52. package/dist/command/cli/lifecycle/daemon-lifecycle.js +11 -2
  53. package/dist/command/cli/mcp-command.d.ts +6 -0
  54. package/dist/command/cli/mcp-command.js +13 -5
  55. package/dist/command/cli/ports/port-holder.js +1 -1
  56. package/dist/command/cli/project-name.d.ts +29 -1
  57. package/dist/command/cli/project-name.js +107 -2
  58. package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
  59. package/dist/command/cli/setup-mcp-cli.js +29 -8
  60. package/dist/command/cli/status/status-lines.d.ts +1 -0
  61. package/dist/command/cli/status/status-lines.js +47 -2
  62. package/dist/command/cli/status/workspace-links.d.ts +10 -0
  63. package/dist/command/cli/status/workspace-links.js +29 -0
  64. package/dist/command/cli/try-command.d.ts +96 -0
  65. package/dist/command/cli/try-command.js +362 -0
  66. package/dist/command/cli/tutorial.d.ts +0 -100
  67. package/dist/command/cli/tutorial.js +0 -178
  68. package/dist/command/cli/verify-results.d.ts +19 -0
  69. package/dist/command/cli/verify-results.js +29 -0
  70. package/dist/command/cli-onboarding.d.ts +0 -11
  71. package/dist/command/cli-onboarding.js +0 -15
  72. package/dist/command/cli.d.ts +14 -0
  73. package/dist/command/cli.js +103 -83
  74. package/dist/command/daemon/daemon-resilience.js +4 -4
  75. package/dist/command/daemon/daemon-resolve.d.ts +9 -1
  76. package/dist/command/daemon/daemon-resolve.js +49 -10
  77. package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
  78. package/dist/command/setup/bringup/bridge-port.js +13 -3
  79. package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
  80. package/dist/command/setup/bringup/ensure-daemon.js +9 -2
  81. package/dist/command/setup/init/agent-restart.d.ts +23 -0
  82. package/dist/command/setup/init/agent-restart.js +28 -0
  83. package/dist/command/setup/init/init-port.d.ts +10 -1
  84. package/dist/command/setup/init/init-port.js +22 -2
  85. package/dist/command/setup/init/init-runtime.d.ts +12 -2
  86. package/dist/command/setup/init/init-runtime.js +96 -37
  87. package/dist/command/setup/node-effects.d.ts +2 -0
  88. package/dist/command/setup/node-effects.js +36 -8
  89. package/dist/command/setup/probe/existing-app.d.ts +12 -0
  90. package/dist/command/setup/probe/existing-app.js +14 -0
  91. package/dist/command/setup/remaining-steps.js +1 -1
  92. package/dist/command/setup/run-setup.d.ts +29 -9
  93. package/dist/command/setup/run-setup.js +34 -28
  94. package/dist/command/setup/setup-command.d.ts +23 -0
  95. package/dist/command/setup/setup-command.js +57 -4
  96. package/dist/command/setup/setup-mcp.js +25 -2
  97. package/dist/command/setup/tauri-signing.d.ts +19 -0
  98. package/dist/command/setup/tauri-signing.js +79 -0
  99. package/dist/command/setup/terminal/listeners.d.ts +10 -1
  100. package/dist/command/setup/terminal/listeners.js +37 -6
  101. package/dist/command/update/update-nudge.d.ts +9 -0
  102. package/dist/command/update/update-nudge.js +1 -1
  103. package/dist/command/update/updater.js +11 -15
  104. package/dist/command/version/version-nudge.js +3 -0
  105. package/dist/command/version/version-skew.d.ts +20 -1
  106. package/dist/command/version/version-skew.js +38 -2
  107. package/dist/features/crawl/crawl.js +14 -1
  108. package/dist/features/exhaust/ledger.d.ts +14 -14
  109. package/dist/features/harness/drive-report.d.ts +39 -5
  110. package/dist/features/harness/drive-report.js +271 -54
  111. package/dist/features/harness/drive-runs.d.ts +120 -0
  112. package/dist/features/harness/drive-runs.js +289 -0
  113. package/dist/features/harness/drivers.d.ts +1 -1
  114. package/dist/features/harness/drivers.js +1 -1
  115. package/dist/features/harness/explore/console-pages.d.ts +15 -0
  116. package/dist/features/harness/explore/console-pages.js +146 -0
  117. package/dist/features/harness/explore/disparity.d.ts +19 -0
  118. package/dist/features/harness/explore/disparity.js +71 -0
  119. package/dist/features/harness/explore/fake-app.d.ts +16 -0
  120. package/dist/features/harness/explore/fake-app.js +49 -0
  121. package/dist/features/harness/explore/goal-search.d.ts +26 -0
  122. package/dist/features/harness/explore/goal-search.js +144 -0
  123. package/dist/features/harness/explore/page-tree.d.ts +49 -0
  124. package/dist/features/harness/explore/page-tree.js +148 -0
  125. package/dist/features/harness/explore/site-map.d.ts +34 -0
  126. package/dist/features/harness/explore/site-map.js +150 -0
  127. package/dist/features/harness/goals.d.ts +68 -7
  128. package/dist/features/harness/goals.js +289 -36
  129. package/dist/features/harness/harness.d.ts +30 -1
  130. package/dist/features/harness/harness.js +141 -7
  131. package/dist/features/harness/platform/local-apps.d.ts +36 -3
  132. package/dist/features/harness/platform/local-apps.js +81 -5
  133. package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
  134. package/dist/features/harness/platform/persona-credentials.js +69 -0
  135. package/dist/features/harness/platform/platform-drives.d.ts +61 -0
  136. package/dist/features/harness/platform/platform-drives.js +114 -0
  137. package/dist/features/harness/platform/refusal.d.ts +52 -0
  138. package/dist/features/harness/platform/refusal.js +71 -0
  139. package/dist/features/harness/platform/remote-drive.d.ts +8 -2
  140. package/dist/features/harness/platform/remote-drive.js +22 -9
  141. package/dist/features/harness/platform/script.d.ts +57 -11
  142. package/dist/features/harness/platform/script.js +144 -12
  143. package/dist/features/harness/platform/server-driver.d.ts +17 -4
  144. package/dist/features/harness/platform/server-driver.js +29 -26
  145. package/dist/features/harness/platform-config.d.ts +38 -5
  146. package/dist/features/harness/platform-config.js +93 -8
  147. package/dist/features/harness/route-guess.d.ts +13 -0
  148. package/dist/features/harness/route-guess.js +49 -0
  149. package/dist/features/harness/script/script-run.d.ts +30 -1
  150. package/dist/features/harness/script/script-run.js +355 -48
  151. package/dist/features/license/license.d.ts +2 -2
  152. package/dist/features/visual/step-shots.d.ts +31 -0
  153. package/dist/features/visual/step-shots.js +231 -0
  154. package/dist/features/visual/visual-tools.js +22 -14
  155. package/dist/hooks/client-directory.d.ts +18 -0
  156. package/dist/hooks/client-directory.js +57 -0
  157. package/dist/hooks/coding-agents.d.ts +34 -0
  158. package/dist/hooks/coding-agents.js +117 -0
  159. package/dist/hooks/driven-by.d.ts +21 -2
  160. package/dist/hooks/driven-by.js +42 -2
  161. package/dist/hooks/hook-bus.d.ts +4 -0
  162. package/dist/hooks/hook-bus.js +18 -0
  163. package/dist/index.d.ts +1 -0
  164. package/dist/index.js +26 -183
  165. package/dist/judgement/capsule/capsule-store.d.ts +2 -2
  166. package/dist/judgement/capsule/causal-summary.d.ts +15 -1
  167. package/dist/judgement/capsule/causal-summary.js +33 -0
  168. package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
  169. package/dist/judgement/runs/artifact/build-verification-run.js +20 -4
  170. package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
  171. package/dist/judgement/runs/artifact/gap-summary.js +12 -3
  172. package/dist/judgement/runs/artifact/replay-mapping.js +25 -9
  173. package/dist/judgement/runs/artifact/run-store.d.ts +13 -1
  174. package/dist/judgement/runs/artifact/run-store.js +175 -8
  175. package/dist/judgement/runs/context-tools.js +59 -1
  176. package/dist/judgement/runs/default-run-id.d.ts +3 -0
  177. package/dist/judgement/runs/default-run-id.js +16 -0
  178. package/dist/judgement/runs/drive-run.d.ts +53 -5
  179. package/dist/judgement/runs/drive-run.js +234 -20
  180. package/dist/judgement/runs/resume.d.ts +44 -0
  181. package/dist/judgement/runs/resume.js +137 -0
  182. package/dist/judgement/runs/reticle-runner.js +5 -2
  183. package/dist/judgement/runs/runner-port.d.ts +5 -1
  184. package/dist/judgement/runs/runner-port.js +8 -2
  185. package/dist/judgement/runs/verification-sync.d.ts +2 -0
  186. package/dist/judgement/runs/verification-sync.js +36 -15
  187. package/dist/language/flows/decision.js +2 -1
  188. package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
  189. package/dist/language/flows/fields/flow-secret-field.js +25 -2
  190. package/dist/language/flows/flow-anchor.d.ts +7 -2
  191. package/dist/language/flows/flow-anchor.js +10 -3
  192. package/dist/language/flows/flow-intent.d.ts +1 -0
  193. package/dist/language/flows/flow-journey.d.ts +6 -0
  194. package/dist/language/flows/flow-journey.js +20 -0
  195. package/dist/language/flows/flow-learning.d.ts +4 -0
  196. package/dist/language/flows/flow-learning.js +37 -3
  197. package/dist/language/flows/flow-replay-run.js +16 -18
  198. package/dist/language/flows/flow-replay-types.d.ts +1 -1
  199. package/dist/language/flows/flow-replay.d.ts +18 -1
  200. package/dist/language/flows/flow-replay.js +22 -7
  201. package/dist/language/flows/flow-result.d.ts +22 -1
  202. package/dist/language/flows/flow-result.js +47 -2
  203. package/dist/language/flows/flow-step-runners.d.ts +2 -0
  204. package/dist/language/flows/flow-step-runners.js +12 -6
  205. package/dist/language/flows/flow-store-for-session.js +5 -1
  206. package/dist/language/flows/flow-tools.d.ts +10 -0
  207. package/dist/language/flows/flow-tools.js +61 -62
  208. package/dist/language/flows/flows.d.ts +16 -1
  209. package/dist/language/flows/flows.js +69 -10
  210. package/dist/language/flows/narrow-write.d.ts +7 -0
  211. package/dist/language/flows/narrow-write.js +36 -1
  212. package/dist/language/flows/recording/recording-backtrack.js +14 -1
  213. package/dist/language/flows/recording/tape/recordings.d.ts +29 -1
  214. package/dist/language/flows/recording/tape/recordings.js +60 -10
  215. package/dist/language/flows/replay.js +29 -6
  216. package/dist/language/flows/single-replay.d.ts +7 -0
  217. package/dist/language/flows/single-replay.js +26 -0
  218. package/dist/language/flows/verify-change-tools.d.ts +1 -0
  219. package/dist/language/flows/verify-change-tools.js +200 -193
  220. package/dist/memory/cloud/cloud-config.d.ts +10 -1
  221. package/dist/memory/cloud/cloud-config.js +19 -5
  222. package/dist/memory/cloud/cloud-sync.js +3 -3
  223. package/dist/memory/cloud/harness-config.d.ts +49 -0
  224. package/dist/memory/cloud/harness-config.js +16 -20
  225. package/dist/memory/cloud/memory-scope.d.ts +17 -0
  226. package/dist/memory/cloud/memory-scope.js +22 -0
  227. package/dist/memory/cloud/sync-cycle.d.ts +32 -4
  228. package/dist/memory/cloud/sync-cycle.js +113 -12
  229. package/dist/memory/cloud/sync-daemon.d.ts +11 -0
  230. package/dist/memory/cloud/sync-daemon.js +114 -4
  231. package/dist/memory/cloud/sync-disk.js +26 -2
  232. package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
  233. package/dist/memory/cloud/sync-screenshots.js +105 -0
  234. package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
  235. package/dist/memory/cloud/unsynced-roots.js +166 -0
  236. package/dist/memory/impact/account-watch.d.ts +16 -1
  237. package/dist/memory/impact/account-watch.js +26 -1
  238. package/dist/memory/impact/impact-store.d.ts +1 -4
  239. package/dist/memory/impact/impact-store.js +32 -42
  240. package/dist/memory/intent/inline-intent.d.ts +1 -0
  241. package/dist/memory/intent/intent-shard-store.js +16 -4
  242. package/dist/memory/intent/intent-shard.d.ts +12 -0
  243. package/dist/memory/intent/intent-store.d.ts +1 -0
  244. package/dist/memory/intent/intent-tools.js +42 -2
  245. package/dist/memory/intent/open-intents.d.ts +6 -0
  246. package/dist/memory/intent/open-intents.js +10 -1
  247. package/dist/memory/intent/settled-words.d.ts +24 -0
  248. package/dist/memory/intent/settled-words.js +130 -0
  249. package/dist/memory/journal/drive-flow.d.ts +16 -0
  250. package/dist/memory/journal/drive-flow.js +34 -2
  251. package/dist/memory/journal/drive-run-flush.d.ts +2 -0
  252. package/dist/memory/journal/drive-run-flush.js +20 -5
  253. package/dist/memory/journal/journal-recorder.js +46 -3
  254. package/dist/memory/journal/on-disk/retention.d.ts +5 -0
  255. package/dist/memory/journal/on-disk/retention.js +27 -2
  256. package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
  257. package/dist/memory/journal/session-end.d.ts +15 -1
  258. package/dist/memory/journal/session-end.js +38 -7
  259. package/dist/memory/project/artifact-root-resolver.js +33 -5
  260. package/dist/memory/project/artifact-root.d.ts +4 -3
  261. package/dist/memory/project/artifact-root.js +2 -2
  262. package/dist/memory/project/dir/reticle-dir.d.ts +14 -0
  263. package/dist/memory/project/dir/reticle-dir.js +23 -2
  264. package/dist/memory/project/link-directory.d.ts +15 -0
  265. package/dist/memory/project/link-directory.js +44 -0
  266. package/dist/memory/project/root-claims.d.ts +6 -0
  267. package/dist/memory/project/root-claims.js +39 -0
  268. package/dist/memory/project/session-root.d.ts +2 -0
  269. package/dist/memory/project/session-root.js +9 -1
  270. package/dist/memory/project/sync-status.d.ts +46 -0
  271. package/dist/memory/project/sync-status.js +135 -1
  272. package/dist/memory/recall/memory-tools.js +46 -3
  273. package/dist/memory/recall/platform-memory.d.ts +34 -0
  274. package/dist/memory/recall/platform-memory.js +139 -0
  275. package/dist/memory/recall/project-memory.js +36 -8
  276. package/dist/portal/bridge/bridge.d.ts +9 -5
  277. package/dist/portal/bridge/bridge.js +66 -23
  278. package/dist/portal/bridge/harness-request.d.ts +23 -0
  279. package/dist/portal/bridge/harness-request.js +27 -0
  280. package/dist/portal/bridge/wrong-path.d.ts +17 -0
  281. package/dist/portal/bridge/wrong-path.js +35 -0
  282. package/dist/portal/input/network-mock-tools.js +11 -2
  283. package/dist/portal/input/real-input.d.ts +2 -0
  284. package/dist/portal/input/real-input.js +13 -10
  285. package/dist/portal/pool/browser-pool.d.ts +48 -0
  286. package/dist/portal/pool/browser-pool.js +120 -2
  287. package/dist/portal/pool/context-permissions.d.ts +33 -0
  288. package/dist/portal/pool/context-permissions.js +33 -0
  289. package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
  290. package/dist/portal/pool/lease-marker-stamp.js +49 -0
  291. package/dist/portal/pool/playwright-launcher.d.ts +27 -5
  292. package/dist/portal/pool/playwright-launcher.js +59 -14
  293. package/dist/portal/pool/pool-contract.d.ts +21 -1
  294. package/dist/portal/session/agent-link.d.ts +8 -0
  295. package/dist/portal/session/agent-link.js +17 -0
  296. package/dist/portal/session/command-payload.js +6 -1
  297. package/dist/portal/session/daemon-answers.d.ts +3 -3
  298. package/dist/portal/session/daemon-answers.js +4 -2
  299. package/dist/portal/session/dev-server/dev-command.d.ts +9 -0
  300. package/dist/portal/session/dev-server/dev-command.js +27 -1
  301. package/dist/portal/session/facts/observed-state.d.ts +16 -18
  302. package/dist/portal/session/facts/observed-state.js +46 -3
  303. package/dist/portal/session/human/live-control.d.ts +9 -2
  304. package/dist/portal/session/human/live-control.js +12 -3
  305. package/dist/portal/session/live-control-tools.js +82 -13
  306. package/dist/portal/session/narration-args.d.ts +5 -0
  307. package/dist/portal/session/narration-args.js +17 -0
  308. package/dist/portal/session/no-session-diagnosis.d.ts +1 -1
  309. package/dist/portal/session/no-session-diagnosis.js +31 -9
  310. package/dist/portal/session/no-session-next-action.d.ts +20 -0
  311. package/dist/portal/session/no-session-next-action.js +134 -61
  312. package/dist/portal/session/no-session-watch.js +179 -22
  313. package/dist/portal/session/presence/session-lease.js +1 -1
  314. package/dist/portal/session/recorded-gaps.d.ts +97 -0
  315. package/dist/portal/session/recorded-gaps.js +251 -0
  316. package/dist/portal/session/session-health.d.ts +11 -0
  317. package/dist/portal/session/session-health.js +3 -1
  318. package/dist/portal/session/session-info.js +2 -1
  319. package/dist/portal/session/session-manager.d.ts +18 -0
  320. package/dist/portal/session/session-manager.js +54 -3
  321. package/dist/portal/session/session-successor.d.ts +8 -0
  322. package/dist/portal/session/session-successor.js +1 -1
  323. package/dist/portal/session/session.d.ts +15 -28
  324. package/dist/portal/session/session.js +60 -61
  325. package/dist/portal/session/timing/command-timeout.js +25 -1
  326. package/dist/portal/session/timing/wall-clock.d.ts +1 -1
  327. package/dist/portal/session/timing/wall-clock.js +1 -1
  328. package/dist/portal/session/zero-install-first-call.d.ts +31 -0
  329. package/dist/portal/session/zero-install-first-call.js +88 -0
  330. package/dist/start-options.d.ts +7 -1
  331. package/dist/start-wiring.d.ts +63 -0
  332. package/dist/start-wiring.js +169 -0
  333. package/dist/status-payload.d.ts +13 -1
  334. package/dist/status-payload.js +13 -1
  335. package/dist/surface/http-server.js +7 -1
  336. package/dist/surface/mcp/attach-memory.d.ts +1 -0
  337. package/dist/surface/mcp/attach-memory.js +12 -1
  338. package/dist/surface/mcp/mcp-proxy.d.ts +4 -0
  339. package/dist/surface/mcp/mcp-proxy.js +29 -8
  340. package/dist/surface/mcp/mcp.d.ts +23 -0
  341. package/dist/surface/mcp/mcp.js +34 -4
  342. package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
  343. package/dist/surface/mcp/proxy/drive-calls.js +27 -0
  344. package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
  345. package/dist/surface/mcp/server-instructions.js +22 -14
  346. package/dist/surface/mcp/shared-params.js +1 -1
  347. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  348. package/dist/surface/mcp/surface-vocabulary.js +1 -0
  349. package/dist/surface/tools/account-tool.d.ts +22 -0
  350. package/dist/surface/tools/account-tool.js +157 -0
  351. package/dist/surface/tools/act/act-danger.d.ts +11 -0
  352. package/dist/surface/tools/act/act-danger.js +38 -1
  353. package/dist/surface/tools/act/act-observation.d.ts +33 -0
  354. package/dist/surface/tools/act/act-observation.js +57 -0
  355. package/dist/surface/tools/act/act-preflight.js +25 -2
  356. package/dist/surface/tools/act/act-target.js +8 -3
  357. package/dist/surface/tools/act/resolve-target.d.ts +1 -1
  358. package/dist/surface/tools/act/resolve-target.js +18 -3
  359. package/dist/surface/tools/act/screen-request.d.ts +34 -0
  360. package/dist/surface/tools/act/screen-request.js +113 -0
  361. package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
  362. package/dist/surface/tools/act/sequence-grade.js +13 -2
  363. package/dist/surface/tools/act-sequence-tool.js +136 -27
  364. package/dist/surface/tools/act-tools.d.ts +0 -9
  365. package/dist/surface/tools/act-tools.js +48 -34
  366. package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
  367. package/dist/surface/tools/agent/app-notes-learning.js +80 -0
  368. package/dist/surface/tools/agent/app-notes.d.ts +4 -0
  369. package/dist/surface/tools/agent/app-notes.js +48 -0
  370. package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
  371. package/dist/surface/tools/agent/build-staleness.js +62 -0
  372. package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
  373. package/dist/surface/tools/agent/compact-envelope.js +374 -0
  374. package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
  375. package/dist/surface/tools/agent/for-the-agent.js +44 -0
  376. package/dist/surface/tools/agent/notices.d.ts +50 -0
  377. package/dist/surface/tools/agent/notices.js +152 -0
  378. package/dist/surface/tools/agent/reteach.d.ts +11 -0
  379. package/dist/surface/tools/agent/reteach.js +50 -0
  380. package/dist/surface/tools/args/alias-args.d.ts +5 -0
  381. package/dist/surface/tools/args/alias-args.js +10 -0
  382. package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
  383. package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
  384. package/dist/surface/tools/assert/assert-source.d.ts +15 -0
  385. package/dist/surface/tools/assert/assert-source.js +40 -0
  386. package/dist/surface/tools/assert/assert-verdict.js +25 -15
  387. package/dist/surface/tools/browser-tools.js +43 -1
  388. package/dist/surface/tools/chat-drives.d.ts +75 -0
  389. package/dist/surface/tools/chat-drives.js +265 -21
  390. package/dist/surface/tools/coverage-tools.d.ts +2 -10
  391. package/dist/surface/tools/coverage-tools.js +2 -53
  392. package/dist/surface/tools/dynamic-tools.js +29 -1
  393. package/dist/surface/tools/empty-session-list.d.ts +10 -0
  394. package/dist/surface/tools/empty-session-list.js +45 -5
  395. package/dist/surface/tools/error-recovery.d.ts +8 -0
  396. package/dist/surface/tools/error-recovery.js +69 -5
  397. package/dist/surface/tools/explore-tools.d.ts +23 -2
  398. package/dist/surface/tools/explore-tools.js +377 -74
  399. package/dist/surface/tools/feedback-tools.d.ts +2 -3
  400. package/dist/surface/tools/feedback-tools.js +4 -3
  401. package/dist/surface/tools/gap-novelty.d.ts +3 -1
  402. package/dist/surface/tools/gap-novelty.js +8 -1
  403. package/dist/surface/tools/harness-explore.d.ts +52 -73
  404. package/dist/surface/tools/harness-explore.js +177 -146
  405. package/dist/surface/tools/harness-gate.d.ts +88 -0
  406. package/dist/surface/tools/harness-gate.js +235 -0
  407. package/dist/surface/tools/harness-hunt.d.ts +43 -0
  408. package/dist/surface/tools/harness-hunt.js +215 -0
  409. package/dist/surface/tools/harness-plan.d.ts +5 -0
  410. package/dist/surface/tools/harness-plan.js +7 -0
  411. package/dist/surface/tools/harness-script.d.ts +22 -0
  412. package/dist/surface/tools/harness-script.js +256 -38
  413. package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
  414. package/dist/surface/tools/harness-tab-guard.js +27 -0
  415. package/dist/surface/tools/harness-toolset.d.ts +8 -0
  416. package/dist/surface/tools/harness-toolset.js +27 -4
  417. package/dist/surface/tools/human-notes.d.ts +16 -0
  418. package/dist/surface/tools/human-notes.js +27 -0
  419. package/dist/surface/tools/invoke-tool.d.ts +5 -2
  420. package/dist/surface/tools/invoke-tool.js +220 -93
  421. package/dist/surface/tools/lease-hint.js +26 -5
  422. package/dist/surface/tools/lease-permissions.d.ts +33 -0
  423. package/dist/surface/tools/lease-permissions.js +73 -0
  424. package/dist/surface/tools/lease-readiness.d.ts +38 -0
  425. package/dist/surface/tools/lease-readiness.js +57 -0
  426. package/dist/surface/tools/lease-seed.d.ts +20 -0
  427. package/dist/surface/tools/lease-seed.js +45 -1
  428. package/dist/surface/tools/lease-tools.d.ts +10 -1
  429. package/dist/surface/tools/lease-tools.js +119 -69
  430. package/dist/surface/tools/live-call-text.js +2 -2
  431. package/dist/surface/tools/merge-tools.js +42 -2
  432. package/dist/surface/tools/merged-name-redirect.js +4 -0
  433. package/dist/surface/tools/next-step.d.ts +11 -1
  434. package/dist/surface/tools/next-step.js +73 -18
  435. package/dist/surface/tools/observe-tools.js +43 -5
  436. package/dist/surface/tools/platform-moment.d.ts +60 -0
  437. package/dist/surface/tools/platform-moment.js +132 -4
  438. package/dist/surface/tools/read-tools.js +65 -14
  439. package/dist/surface/tools/reticle-result.d.ts +120 -0
  440. package/dist/surface/tools/reticle-result.js +364 -0
  441. package/dist/surface/tools/tool-kit.d.ts +68 -2
  442. package/dist/surface/tools/tool-kit.js +73 -1
  443. package/dist/surface/tools/tools.js +25 -13
  444. package/dist/telemetry/account-envelope.d.ts +7 -0
  445. package/dist/telemetry/account-envelope.js +61 -0
  446. package/dist/telemetry/agent-client.d.ts +15 -0
  447. package/dist/telemetry/agent-client.js +57 -0
  448. package/dist/telemetry/app-instrumented.js +9 -2
  449. package/dist/telemetry/argument-shape.d.ts +7 -0
  450. package/dist/telemetry/argument-shape.js +86 -1
  451. package/dist/telemetry/automation-hint.d.ts +2 -3
  452. package/dist/telemetry/automation-hint.js +33 -8
  453. package/dist/telemetry/cli-telemetry.js +7 -2
  454. package/dist/telemetry/daemon-telemetry.js +3 -0
  455. package/dist/telemetry/error-fingerprint.d.ts +10 -1
  456. package/dist/telemetry/error-fingerprint.js +61 -5
  457. package/dist/telemetry/feedback-cli.d.ts +0 -13
  458. package/dist/telemetry/feedback-cli.js +0 -77
  459. package/dist/telemetry/feedback.d.ts +13 -0
  460. package/dist/telemetry/feedback.js +183 -17
  461. package/dist/telemetry/harness-run.d.ts +18 -0
  462. package/dist/telemetry/harness-run.js +80 -0
  463. package/dist/telemetry/hud-events.d.ts +16 -0
  464. package/dist/telemetry/hud-events.js +27 -0
  465. package/dist/telemetry/hud-metrics.d.ts +27 -6
  466. package/dist/telemetry/hud-metrics.js +99 -25
  467. package/dist/telemetry/init-telemetry.js +2 -2
  468. package/dist/telemetry/install-source.d.ts +13 -25
  469. package/dist/telemetry/install-source.js +44 -3
  470. package/dist/telemetry/mcp-connection.d.ts +0 -14
  471. package/dist/telemetry/mcp-connection.js +5 -1
  472. package/dist/telemetry/onboarding-firsts.d.ts +32 -0
  473. package/dist/telemetry/onboarding-firsts.js +81 -6
  474. package/dist/telemetry/platform-linked.d.ts +5 -0
  475. package/dist/telemetry/platform-linked.js +32 -0
  476. package/dist/telemetry/project-profile.js +5 -3
  477. package/dist/telemetry/proxy-telemetry.js +4 -1
  478. package/dist/telemetry/session-metrics.d.ts +15 -4
  479. package/dist/telemetry/session-metrics.js +88 -52
  480. package/dist/telemetry/telemetry.d.ts +26 -2
  481. package/dist/telemetry/telemetry.js +102 -21
  482. package/dist/telemetry/verification-of.d.ts +3 -1
  483. package/dist/telemetry/verification-of.js +12 -4
  484. package/dist/wire-journal.d.ts +2 -2
  485. package/dist/wire-journal.js +16 -5
  486. package/docs/README.md +2 -2
  487. package/docs/actions.mdx +6 -3
  488. package/docs/adapters.md +1 -1
  489. package/docs/agent-cheatsheet.md +24 -27
  490. package/docs/architecture.md +5 -5
  491. package/docs/autodrive.md +13 -12
  492. package/docs/benchmarks.md +5 -5
  493. package/docs/best-practices.mdx +27 -9
  494. package/docs/capabilities.mdx +14 -16
  495. package/docs/cli/affected.mdx +1 -1
  496. package/docs/cli/cloud.mdx +54 -75
  497. package/docs/cli/doctor.mdx +8 -3
  498. package/docs/cli/drive.mdx +12 -12
  499. package/docs/cli/feedback.mdx +3 -3
  500. package/docs/cli/gate.mdx +7 -2
  501. package/docs/cli/help.mdx +50 -55
  502. package/docs/cli/init.mdx +32 -15
  503. package/docs/cli/license.mdx +12 -8
  504. package/docs/cli/mcp.mdx +4 -4
  505. package/docs/cli/open.mdx +3 -3
  506. package/docs/cli/report.mdx +4 -2
  507. package/docs/cli/restart.mdx +2 -2
  508. package/docs/cli/rollback.mdx +14 -8
  509. package/docs/cli/serve.mdx +4 -4
  510. package/docs/cli/setup.mdx +23 -0
  511. package/docs/cli/status.mdx +13 -4
  512. package/docs/cli/stop.mdx +67 -8
  513. package/docs/cli/try.mdx +62 -0
  514. package/docs/cli/update.mdx +4 -3
  515. package/docs/cli/verify.mdx +17 -6
  516. package/docs/cli/version.mdx +3 -2
  517. package/docs/cli.mdx +110 -41
  518. package/docs/debugging.md +3 -3
  519. package/docs/deploy-checks.md +1 -1
  520. package/docs/desktop.mdx +5 -3
  521. package/docs/enterprise.md +11 -10
  522. package/docs/environment.mdx +5 -2
  523. package/docs/faq.mdx +3 -3
  524. package/docs/first-drive.md +5 -4
  525. package/docs/flows.md +35 -27
  526. package/docs/for-agents.md +4 -5
  527. package/docs/frameworks.mdx +32 -21
  528. package/docs/getting-started.md +66 -64
  529. package/docs/human-control.md +15 -14
  530. package/docs/index.mdx +15 -43
  531. package/docs/install-agentic.mdx +22 -37
  532. package/docs/install-manual.mdx +22 -12
  533. package/docs/instrumentation.mdx +5 -3
  534. package/docs/integration-patterns.md +7 -7
  535. package/docs/license-activation.mdx +1 -1
  536. package/docs/multi-agent-testing.md +24 -6
  537. package/docs/packages/babel-plugin.mdx +4 -2
  538. package/docs/packages/browser.mdx +16 -10
  539. package/docs/packages/core.mdx +12 -5
  540. package/docs/packages/electron.mdx +3 -2
  541. package/docs/packages/engine.mdx +1 -1
  542. package/docs/packages/eslint-plugin.mdx +18 -12
  543. package/docs/packages/next.mdx +8 -7
  544. package/docs/packages/open-verification.mdx +1 -1
  545. package/docs/packages/server.mdx +5 -5
  546. package/docs/packages/tauri.mdx +8 -8
  547. package/docs/packages/test.mdx +4 -10
  548. package/docs/packages/vite-plugin.mdx +11 -4
  549. package/docs/packages.mdx +11 -15
  550. package/docs/platform-integration.md +6 -5
  551. package/docs/predicates.mdx +40 -24
  552. package/docs/quickstart.mdx +58 -29
  553. package/docs/required-checks.md +8 -7
  554. package/docs/reticle-manual.md +15 -5
  555. package/docs/skill-file.mdx +17 -79
  556. package/docs/skill-setup.md +9 -9
  557. package/docs/skill.md +200 -0
  558. package/docs/system-map.md +7 -7
  559. package/docs/telemetry.md +22 -24
  560. package/docs/testing.md +4 -4
  561. package/docs/token-efficiency.md +12 -12
  562. package/docs/tools/act-and-wait.mdx +9 -2
  563. package/docs/tools/act-sequence.mdx +6 -7
  564. package/docs/tools/act.mdx +2 -2
  565. package/docs/tools/assert.mdx +18 -17
  566. package/docs/tools/console.mdx +9 -9
  567. package/docs/tools/inspect.mdx +16 -7
  568. package/docs/tools/look.mdx +24 -0
  569. package/docs/tools/navigate.mdx +5 -3
  570. package/docs/tools/network.mdx +2 -2
  571. package/docs/tools/observe.mdx +6 -6
  572. package/docs/tools/overview.mdx +34 -33
  573. package/docs/tools/query.mdx +5 -5
  574. package/docs/tools/session-and-feedback.mdx +13 -4
  575. package/docs/tools/sessions.mdx +8 -8
  576. package/docs/tools/snapshot.mdx +6 -5
  577. package/docs/tools/state.mdx +9 -9
  578. package/docs/tools/tools-and-run.mdx +26 -24
  579. package/docs/tools/verify.mdx +28 -0
  580. package/docs/tools/wait-for.mdx +8 -4
  581. package/docs/troubleshooting.mdx +21 -6
  582. package/docs/usage.md +51 -45
  583. package/docs/use-cases.mdx +3 -3
  584. package/docs/vs-chrome-devtools-mcp.mdx +9 -6
  585. package/docs/vs-playwright-mcp.mdx +3 -1
  586. package/docs/what-is-recorded.md +7 -5
  587. package/docs/what-is-reticle.mdx +2 -2
  588. package/docs/why-reticle.mdx +1 -1
  589. package/package.json +6 -5
  590. package/dist/command/cli/demo/demo-page.d.ts +0 -78
  591. package/dist/command/cli/demo/demo-page.js +0 -249
  592. package/dist/command/cli/demo/demo-run.d.ts +0 -38
  593. package/dist/command/cli/demo/demo-run.js +0 -138
  594. package/dist/judgement/hunt/hunt-report.d.ts +0 -49
  595. package/dist/judgement/hunt/hunt-report.js +0 -84
  596. package/dist/language/flows/buddy-status.d.ts +0 -25
  597. package/dist/language/flows/buddy-status.js +0 -32
  598. package/dist/language/flows/change/watch-batcher.d.ts +0 -18
  599. package/dist/language/flows/change/watch-batcher.js +0 -19
  600. package/dist/memory/cloud/harness-offer.d.ts +0 -44
  601. package/dist/memory/cloud/harness-offer.js +0 -145
  602. package/dist/surface/tools/feature-capture.d.ts +0 -151
  603. package/dist/surface/tools/feature-capture.js +0 -235
  604. package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
  605. package/dist/surface/tools/tool-hit-rate.js +0 -90
  606. package/dist/telemetry/harness-drive.d.ts +0 -4
  607. package/dist/telemetry/harness-drive.js +0 -35
  608. package/dist/telemetry/identify.d.ts +0 -22
  609. package/dist/telemetry/identify.js +0 -73
  610. package/docs/cli/capsules.mdx +0 -70
  611. package/docs/cli/hunt.mdx +0 -73
  612. package/docs/cli/identify.mdx +0 -76
  613. package/docs/cli/kill.mdx +0 -91
  614. package/docs/cli/watch.mdx +0 -62
@@ -1,4 +1,4 @@
1
- import { ReticleTool } from '@reticlehq/core';
1
+ import { ReticleTool, WRITING_RULE_SHORT } from '@reticlehq/core';
2
2
  import { SHARED_PARAM_GUIDANCE } from './shared-params.js';
3
3
  import { defaultAdvertisedNames } from '../tools/tool-surface.js';
4
4
  import { surfaceVocabulary, listOf } from './surface-vocabulary.js';
@@ -44,9 +44,9 @@ import { surfaceVocabulary, listOf } from './surface-vocabulary.js';
44
44
  */
45
45
  const verdictDiscipline = (v) => `Reticle verifies a running web app from the inside: go (${v.navigate}), look (${listOf(v.look, v.find)}), act and prove in one hop (${v.actAndWait}), observe (${listOf(v.observe, v.state, v.network, v.console)}), assert (${v.assert}). Verify a user-facing change against the real app before you call it done — \`npx @reticlehq/server gate --since <ref>\` fails unless a passing artifact covers every flow your edits affect — and never weaken a check to make it pass.
46
46
 
47
- Only ${v.act.length > 0 ? `${v.actAndWait}, ${v.assert} and ${v.act}{steps}` : `${v.actAndWait} and ${v.assert}`} produce a verdict. Everything else proves nothing; a drive without one has no result. Prefer ${v.actAndWait}({ ref, action, until }) — it names the expected consequence BEFORE the action, which is the difference between a check and a rationalisation. Only verified:"yes" is a pass; "unknown" (could not tell) and "no-fault" (nothing declared) are not proved. When a verdict catches what reading the code missed, say Reticle found it. Do a result's \`next\`.
47
+ Only ${v.act.length > 0 ? `${v.actAndWait}, ${v.assert} and ${v.act}{steps}` : `${v.actAndWait} and ${v.assert}`} produce a verdict. Everything else proves nothing; a drive without one has no result. Prefer ${v.actAndWait}({ ref, action, until }) — it names the expected consequence BEFORE the action, which is the difference between a check and a rationalisation. Only verified:"yes" is a pass; "unknown" and "no-fault" (nothing declared) are not proved, never failed. When a verdict catches what reading the code missed, say Reticle found it. Do a result's \`next\`.
48
48
 
49
- Done is the journey's END proved, not its first step: drive each step your change touches and give the last an \`until\` naming the end state (\`durable: true\` if it must persist). A "yes" on it over a clean capture IS the answer — stop.
49
+ Done is the journey's END proved, not its first step: drive each step your change touches and give the last an \`until\` naming the end state (\`durable: true\` if it must persist). A "yes" on it over a clean capture IS the answer — stop.${v.act.length > 0 ? ` Several steps? ONE ${v.act}{ steps: [{ target, action, expect }] } drives them all and stops at the first expect that fails; the last expect is the end state.` : ''}
50
50
 
51
51
  DIAGNOSING a bug? Read the source first — Reticle proves what the app DOES, not why. Drive to CONFIRM a fix, not to find one.`;
52
52
  /**
@@ -77,8 +77,15 @@ const replayFirst = (v) => {
77
77
  if (0 === v.replayChange.length)
78
78
  return '';
79
79
  const ask = v.affected.length > 0 ? `${v.affected} names the saved flows covering your edits; ` : '';
80
- return `Changed code? Replay before you drive. ${ask}${v.replayChange} replays them for one verified + because — hundreds of tokens where driving the same journey costs tens of thousands. "yes": proved, stop. "no": names the step that broke. "unknown": NO saved flow covers it, so nothing was proved — drive it, and never report it as passing. What you drive by hand is saved as a flow automatically.`;
80
+ return `Changed code? Replay before you drive. ${ask}${v.replayChange} replays them for one verified + because — far cheaper than driving it again. "yes": proved, stop. "no": names the step that broke. "unknown": NO saved flow covers it, so nothing was proved — drive it, and never report it as passing. What you drive by hand is saved as a flow automatically.${driveOnce(v)}`;
81
81
  };
82
+ /**
83
+ * A drive outlives a client's timeout, so its call answers with a run id instead of waiting for the
84
+ * end; an agent that retried instead of polling paid for a second drive.
85
+ */
86
+ const driveOnce = (v) => 0 === v.explore.length
87
+ ? ''
88
+ : ` Drivers: you, or the Harness (${v.explore}; opt-in at 80% instrumentation): poll its runId; never start two.`;
82
89
  /**
83
90
  * The first move, for a project no app has ever connected to.
84
91
  *
@@ -90,12 +97,15 @@ const replayFirst = (v) => {
90
97
  * common way this step half-succeeds. Telling every agent to restart sent them to kill the server
91
98
  * `init` had just started.
92
99
  */
93
- const firstMoveFor = (v, appHere) => `FIRST: no app has ever connected to Reticle in this project, so nothing here can be verified yet. Having these tools is not the same as being set up — the tools reach a daemon, and the daemon has nothing to look at until the app itself is instrumented.
100
+ // The lead is a snapshot, not a present fact: these instructions are sent ONCE, at the handshake, and
101
+ // the app can connect a minute later. Stated as fact, it kept agents running setup while the session
102
+ // list showed the app connected (#1362).
103
+ const firstMoveFor = (v, appHere) => `FIRST: when this session started, no app had connected to Reticle in this project. Having these tools is not the same as being set up — the tools reach a daemon that has nothing to look at until the app is instrumented.
94
104
 
95
- Fix that first: call ${v.sessions}. Its first call wires the app (\`init\`, dev server, browser) and \`wired\` lists the files it changed; tell the user. If it answers \`run_init\`, run that command in the app's directory. A session listed there is the proof, and until one appears no other tool can tell you anything about this app.${appHere ? TELL_THE_USER : ''}`;
105
+ Fix that first: call ${v.sessions}. Its first call wires the app (\`init\`, dev server, browser) and \`wired\` lists the files it changed; tell the user. If it answers \`run_init\`, run that command in the app's directory. A session on this project's app listed there is the proof; until one appears no other tool can tell you anything about this app.${appHere ? TELL_THE_USER : ''}`;
96
106
  const UNWIRED_LEAD_END = 'can tell you anything about this app.';
97
107
  // An agent never asked to verify anything never reads "fix that first"; this makes it raise wiring.
98
- const TELL_THE_USER = ' This directory holds a web app that is not wired yet: in your first reply, even if their request is unrelated, tell the user in one line and offer to wire it (the first session call does it). Do it only if they agree; if they decline, do not raise it again.';
108
+ const TELL_THE_USER = ' If none is listed, this directory holds a web app that is not wired: in your first reply, even if their request is unrelated, tell the user in one line and offer to wire it (the first session call does it). Do it only if they agree; if they decline, do not raise it again.';
99
109
  /** The nudge set for the AGENT's directory: a shared daemon briefs from where IT started. */
100
110
  export function localizeInstructions(instructions, appHere) {
101
111
  const without = instructions.replace(TELL_THE_USER, '');
@@ -130,16 +140,14 @@ const reachFor = (v) => {
130
140
  // surface-vocabulary.ts. Where there is no cold tail, there is nothing to say about reaching it.
131
141
  if (v.coldTail.length > 0) {
132
142
  lines.push(`${v.coldTail} Two to know early: ${ReticleTool.RUN}({ tool: "${ReticleTool.CONTEXT}", args: {} }) returns what this run already established, after a compaction or handover, and ${ReticleTool.RUN}({ tool: "${ReticleTool.INTENT}", args: { action: "declare", ... } }) declares what a change was MEANT to do, so the verdict has something to check against other than itself.`);
143
+ // Measured: in 8 real agent runs asked to test an app, none read what Reticle already held
144
+ // first, and none could tell its user the credits or the Harness state.
145
+ lines.push(`Before testing, read what Reticle knows: ${ReticleTool.RUN}({ tool: "${ReticleTool.MEMORY}", args: {} })${v.yield.length > 0 ? ` and ${ReticleTool.SESSION} {action:"account"} (plan, credits, Harness)` : ''}.`);
133
146
  }
134
147
  return lines.join('\n\n');
135
148
  };
136
149
  /** The feedback ask. Constant: an agent that cannot get set up is the report we most need. */
137
- const feedbackAsk = (v) => `Feedback is first-class here, not a courtesy. Reticle is built FOR agents, so your experience is the only signal that decides what gets built or fixed — and it disappears when your context does. Send it with \`${v.feedback}\` the moment you notice, then carry on with your task:
138
- - Reticle broke, lied, or could not see something you needed (\`bug\` / \`gap\` / \`ambiguity\`).
139
- - You wished it could do something, or something works but costs too many calls (\`feature_request\` / \`improvement\`).
140
- - The install, the wiring, or the setup itself gave you trouble — including anything you had to work around by hand.
141
-
142
- Nothing is too minor, and a report costs one call. If the tools are unreachable (setup unfinished, daemon down), file the same report from the shell instead: \`reticle feedback --agent --kind <one of those kinds> "what happened"\`. Report defects in RETICLE — a bug you find in the app under test is Reticle working, and belongs in your answer to the user.`;
150
+ const feedbackAsk = (v) => `Feedback is first-class here: once per session, when Reticle got in your way, send \`${v.feedback}\` and carry on — it broke, lied or missed something (\`bug\` / \`gap\` / \`ambiguity\`), cost too many calls or lacked something (\`improvement\` / \`feature_request\`), or the install, the wiring, or the setup gave you trouble. Tools unreachable? \`reticle feedback --agent --kind <kind> "what happened"\`. A bug in the app under test belongs in your answer to the user.`;
143
151
  /** The instructions this daemon should advertise, given what it knows about the project. */
144
152
  /**
145
153
  * Instructions are sent ONCE at initialize; the tool surface is re-sent every turn, and costs an
@@ -175,5 +183,5 @@ export function buildServerInstructions(state) {
175
183
  ? `${verdictDiscipline(v)}\n\n${reachFor(v)}\n\n${feedbackAsk(v)}`
176
184
  : `${firstMove}\n\n${verdictDiscipline(v)}\n\n${feedbackAsk(v)}`;
177
185
  const withReplay = replay.length > 0 ? `${base}\n\n${replay}` : base;
178
- return `${withReplay}\n\n${SHARED_PARAM_GUIDANCE}`;
186
+ return `${withReplay}\n\n${SHARED_PARAM_GUIDANCE}\n\n${WRITING_RULE_SHORT}`;
179
187
  }
@@ -29,7 +29,7 @@ export const SHARED_PARAM_SHORT = {
29
29
  export const SHARED_PARAM_GUIDANCE = 'Arguments that mean the same thing on every tool: `sessionId` — omit unless you mean a specific ' +
30
30
  'tab; Reticle scopes to your project, prefers the active one, and refuses rather than guesses when ' +
31
31
  'ambiguous. `since` — a cursor from a prior act/observe, bounding an observation to what came ' +
32
- 'after it, or a git ref when verifying a change. `limit` — caps returned descriptors, cutting tokens on broad queries.';
32
+ 'after it, or a git ref when verifying a change. `limit` — caps returned descriptors.';
33
33
  /** Is this a parameter whose guidance now lives in the instructions block? */
34
34
  export function isSharedParam(name) {
35
35
  return Object.hasOwn(SHARED_PARAM_SHORT, name);
@@ -41,6 +41,8 @@ export interface SurfaceVocabulary {
41
41
  replayAll: string;
42
42
  /** Rebind a drifted locator, or empty when the surface cannot reach it. */
43
43
  heal: string;
44
+ /** Start or poll a Harness drive, or empty when the surface cannot reach it. */
45
+ explore: string;
44
46
  }
45
47
  /** Drop the empty slots, so a sentence never reads "look (), find ()". */
46
48
  export declare function listOf(...calls: readonly string[]): string;
@@ -52,5 +52,6 @@ export function surfaceVocabulary(advertisedNames) {
52
52
  replayChange: call(ReticleTool.VERIFY_CHANGE, ReticleTool.VERIFY, 'change'),
53
53
  replayAll: call(ReticleTool.FLOW_REPLAY, ReticleTool.VERIFY, 'flows'),
54
54
  heal: advertised.has(ReticleTool.FLOW_HEAL) ? ReticleTool.FLOW_HEAL : '',
55
+ explore: call(ReticleTool.VERIFY_EXPLORE, ReticleTool.VERIFY, 'explore'),
55
56
  };
56
57
  }
@@ -0,0 +1,22 @@
1
+ import type { ToolDef } from './tool-kit.js';
2
+ /** What the agent is told to do next, in plain words: one step, the first one that applies. */
3
+ export declare const ACCOUNT_NEXT: {
4
+ readonly SIGN_IN: "The user is not signed in to Reticle. Ask them to run `reticle connect` in the app folder: it signs them in and links this project. A new account gets 10 free credits.";
5
+ readonly LINK: "This project does not report to the dashboard yet. Ask the user to press \"Link this project\" on the Harness page of the HUD, or to run `reticle connect` in the app folder.";
6
+ readonly UNREACHABLE: "The Reticle platform did not answer, so credits and the Harness are not known now. Local checks still work.";
7
+ readonly NO_CREDITS: (said: string) => string;
8
+ readonly locked: (percent: number, missing: string) => string;
9
+ readonly OFF: "Reticle Harness is off for this project. The user can switch it on in the HUD (the Harness row at the foot of the Agent Log) or on the dashboard.";
10
+ readonly NO_PROVIDER: "The platform has no model provider set, so Reticle Harness cannot run now. Tell the user; it is not on their side.";
11
+ readonly ready: (typical: number | undefined, max: number | undefined) => string;
12
+ };
13
+ export interface AccountInput {
14
+ signedIn: boolean;
15
+ workspace?: string;
16
+ dashboardUrl?: string;
17
+ /** The platform's `/v1/model/config` answer; undefined when unlinked, null when it did not answer. */
18
+ platform: Record<string, unknown> | null | undefined;
19
+ }
20
+ /** The facts and the next step. Pure: the tool's I/O is in `readAccount`. */
21
+ export declare function accountFacts(input: AccountInput): Record<string, unknown>;
22
+ export declare const ACCOUNT_TOOLS: ToolDef[];
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Where the user stands with Reticle, for the coding agent: signed in or not, whether this project
3
+ * reports to the dashboard, the plan and credits left, whether Reticle Harness is on and unlocked,
4
+ * what a Harness run costs, and the one thing to do next.
5
+ *
6
+ * In 16 real Claude Code and Codex runs, no agent could answer "how many credits do I have" or "can
7
+ * the Harness run" from a tool: those facts lived only in the HUD and the dashboard. This reads the
8
+ * same sources they do (the HUD's snapshot for the account, the platform's model config for the
9
+ * plan, credits and gate), so the agent and the HUD cannot disagree.
10
+ */
11
+ import { z } from 'zod';
12
+ import { ReticleTool, asRecord, asString } from '@reticlehq/core';
13
+ import { creditsUsageText } from '@reticlehq/core/hud';
14
+ import { impactSnapshot } from '../../memory/impact/impact-recorder.js';
15
+ import { readDashboardUrl } from '../../memory/project/sync-status.js';
16
+ import { sessionRoot } from '../../memory/project/session-root.js';
17
+ import { platformHeaders } from '../../features/harness/platform/server-driver.js';
18
+ const CONFIG_PATH = '/v1/model/config';
19
+ const CONFIG_TIMEOUT_MS = 3_000;
20
+ const UNLOCK_PERCENT = 80;
21
+ /** What the agent is told to do next, in plain words: one step, the first one that applies. */
22
+ export const ACCOUNT_NEXT = {
23
+ SIGN_IN: 'The user is not signed in to Reticle. Ask them to run `reticle connect` in the app folder: it signs them in and links this project. A new account gets 10 free credits.',
24
+ LINK: 'This project does not report to the dashboard yet. Ask the user to press "Link this project" on the Harness page of the HUD, or to run `reticle connect` in the app folder.',
25
+ UNREACHABLE: 'The Reticle platform did not answer, so credits and the Harness are not known now. Local checks still work.',
26
+ NO_CREDITS: (said) => `${said} Reticle Harness cannot run until the user gets more credits on the dashboard's Plan and usage page.`,
27
+ locked: (percent, missing) =>
28
+ // The platform's own sentence already names the score and the gaps: said once, not twice.
29
+ `${0 < missing.length ? missing : `Setup score ${String(percent)}%: Reticle Harness needs ${String(UNLOCK_PERCENT)}%.`} Offer to close these gaps, then drive one flow to raise the score.`,
30
+ OFF: 'Reticle Harness is off for this project. The user can switch it on in the HUD (the Harness row at the foot of the Agent Log) or on the dashboard.',
31
+ NO_PROVIDER: 'The platform has no model provider set, so Reticle Harness cannot run now. Tell the user; it is not on their side.',
32
+ ready: (typical, max) => 'Reticle Harness is on and ready: reticle_verify { action: "explore", persona: "<the journey, with the text the page must show at the end in quotes>" }' +
33
+ (typical === undefined
34
+ ? '.'
35
+ : `. A run costs about ${String(typical)} credits${max === undefined ? '' : `, at most ${String(max)}`}.`),
36
+ };
37
+ const num = (v) => ('number' === typeof v ? v : undefined);
38
+ const str = (v) => ('string' === typeof v ? v : undefined);
39
+ /** The facts and the next step. Pure: the tool's I/O is in `readAccount`. */
40
+ export function accountFacts(input) {
41
+ const base = {
42
+ signedIn: input.signedIn,
43
+ ...(input.workspace === undefined ? {} : { workspace: input.workspace }),
44
+ linked: input.platform !== undefined,
45
+ ...(input.dashboardUrl === undefined ? {} : { dashboardUrl: input.dashboardUrl }),
46
+ };
47
+ if (!input.signedIn)
48
+ return { ...base, next: ACCOUNT_NEXT.SIGN_IN };
49
+ if (input.platform === undefined)
50
+ return { ...base, next: ACCOUNT_NEXT.LINK };
51
+ if (null === input.platform)
52
+ return { ...base, next: ACCOUNT_NEXT.UNREACHABLE };
53
+ const p = input.platform;
54
+ const rawCredits = asRecord(p['credits']);
55
+ const used = num(rawCredits['used']);
56
+ const limit = num(rawCredits['limit']);
57
+ const plan = asRecord(p['plan']);
58
+ const daysLeft = num(plan['daysLeft']);
59
+ const kind = str(rawCredits['kind']);
60
+ const credits = used === undefined || limit === undefined
61
+ ? undefined
62
+ : {
63
+ used,
64
+ limit,
65
+ left: Math.max(0, limit - used),
66
+ ...(kind === undefined ? {} : { kind }),
67
+ said: creditsUsageText({
68
+ used,
69
+ limit,
70
+ ...('free' === kind || 'trial' === kind || 'paid' === kind ? { kind } : {}),
71
+ ...(daysLeft === undefined ? {} : { daysLeft }),
72
+ }),
73
+ };
74
+ const runCost = asRecord(p['harnessRunCredits']);
75
+ const typical = num(runCost['typical']);
76
+ const max = num(runCost['max']);
77
+ const lock = asRecord(p['harnessLock']);
78
+ const score = num(p['coverageScore']);
79
+ const percent = score === undefined ? undefined : Math.floor(score * 100 + 1e-9);
80
+ const unlocked = true === p['harnessUnlocked'];
81
+ const provider = str(p['provider']);
82
+ const providerReady = provider === undefined ? true : asRecord(p['available'])[provider] !== false;
83
+ const harness = {
84
+ on: true === p['harnessEnabled'],
85
+ unlocked,
86
+ ...(percent === undefined ? {} : { setupScore: percent }),
87
+ ...(str(lock['reason']) === undefined ? {} : { missing: str(lock['reason']) }),
88
+ ...(str(lock['prompt']) === undefined ? {} : { setupPrompt: str(lock['prompt']) }),
89
+ ...(typical === undefined
90
+ ? {}
91
+ : { runCredits: { typical, ...(max === undefined ? {} : { max }) } }),
92
+ };
93
+ const facts = {
94
+ ...base,
95
+ ...(0 === Object.keys(plan).length ? {} : { plan }),
96
+ ...(credits === undefined ? {} : { credits }),
97
+ harness,
98
+ };
99
+ const next = credits !== undefined && 0 === credits.left
100
+ ? ACCOUNT_NEXT.NO_CREDITS(credits.said)
101
+ : !unlocked
102
+ ? ACCOUNT_NEXT.locked(percent ?? 0, str(lock['reason']) ?? '')
103
+ : !providerReady
104
+ ? ACCOUNT_NEXT.NO_PROVIDER
105
+ : !harness.on
106
+ ? ACCOUNT_NEXT.OFF
107
+ : ACCOUNT_NEXT.ready(typical, max);
108
+ return { ...facts, next };
109
+ }
110
+ /** The platform's model config with this project's linked key; undefined unlinked, null on no answer. */
111
+ async function platformConfig(deps) {
112
+ // The project's own link, not the environment: an agent asks about the project it is in.
113
+ const platform = (await deps.linkedCloud?.()) ?? null;
114
+ if (null === platform)
115
+ return undefined;
116
+ try {
117
+ const res = await fetch(`${platform.url.replace(/\/+$/, '')}${CONFIG_PATH}`, {
118
+ method: 'GET',
119
+ headers: platformHeaders(platform),
120
+ signal: AbortSignal.timeout(CONFIG_TIMEOUT_MS),
121
+ });
122
+ if (!res.ok)
123
+ return null;
124
+ return asRecord(JSON.parse(await res.text()));
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ }
130
+ async function readAccount(deps, sessionId) {
131
+ let root;
132
+ try {
133
+ root = sessionRoot(deps, sessionId);
134
+ }
135
+ catch {
136
+ root = undefined; // no tab: the account still answers, without a project
137
+ }
138
+ const snapshot = impactSnapshot(root);
139
+ const account = snapshot?.account;
140
+ const dashboardUrl = root === undefined ? undefined : readDashboardUrl(root);
141
+ return accountFacts({
142
+ signedIn: true === account?.signedIn,
143
+ ...(account?.org === undefined ? {} : { workspace: account.org }),
144
+ ...(dashboardUrl === undefined ? {} : { dashboardUrl }),
145
+ platform: await platformConfig(deps),
146
+ });
147
+ }
148
+ export const ACCOUNT_TOOLS = [
149
+ {
150
+ name: ReticleTool.ACCOUNT,
151
+ description: 'Where the user stands with Reticle: signed in or not, whether this project reports to the dashboard, the plan and credits left, whether Reticle Harness is on and unlocked, the setup score and what is missing, what a Harness run costs, and the one thing to do next. Read it before you tell the user anything about credits, the plan or the Harness.',
152
+ inputSchema: {
153
+ sessionId: z.string().optional().describe('Target a specific tab. Omit unless you mean one.'),
154
+ },
155
+ handler: async (deps, args) => readAccount(deps, asString(args['sessionId'])),
156
+ },
157
+ ];
@@ -18,3 +18,14 @@ export declare function assertDragNotDestructive(innerArgs: Record<string, unkno
18
18
  export declare const NATIVE_INPUT_UNSUPPORTED: string;
19
19
  /** Refuse rather than silently drop a native-input request the act-then-wait path cannot honour. */
20
20
  export declare function assertNativeInputSupported(innerArgs: Record<string, unknown>): void;
21
+ /**
22
+ * The agent's own declaration counts as the confirmation, for that one control.
23
+ *
24
+ * `reticle_act_and_wait { target: { role: "button", name: "Place order" }, until }` names the
25
+ * control by the very words the guard flags AND names the consequence before acting: asking again
26
+ * for `confirmDangerous` cost a round trip per step on a shop's ordinary path and protected nothing.
27
+ * A `ref` or `testid` does not count — neither says what the control does, and a stale ref is
28
+ * exactly how the wrong button gets pressed. A Harness call never takes this path: its outward
29
+ * guard decides alone.
30
+ */
31
+ export declare function confirmedByDeclaration(innerArgs: Record<string, unknown>, target: unknown, declared: boolean): Record<string, unknown>;
@@ -1,4 +1,5 @@
1
- import { ActionType, DANGEROUS_ACTION_CONFIRM_ARG, NATIVE_INPUT_ARG, isDangerousActionText, } from '@reticlehq/core';
1
+ import { ActionType, DANGEROUS_ACTION_CONFIRM_ARG, NATIVE_INPUT_ARG, isDangerousActionText, outwardActionOf, outwardRefusal, } from '@reticlehq/core';
2
+ import { currentDrivenBy, harnessOutwardAllowed } from '../../../hooks/driven-by.js';
2
3
  import { asRecord, asString } from '@reticlehq/core';
3
4
  /**
4
5
  * Refuse to drive a money-moving or destructive control by accident.
@@ -29,9 +30,21 @@ function descriptorRole(value) {
29
30
  function isDestructiveDescriptor(value) {
30
31
  return isDangerousActionText(descriptorText(value), descriptorRole(value));
31
32
  }
33
+ /** A Harness call presses an outward control only when its journey asked (`harnessOutwardAllowed`). */
34
+ function assertOutwardAllowed(...descriptors) {
35
+ const allowed = harnessOutwardAllowed();
36
+ if (allowed === undefined)
37
+ return;
38
+ for (const descriptor of descriptors) {
39
+ const kind = outwardActionOf(descriptorText(descriptor), descriptorRole(descriptor));
40
+ if (kind !== undefined && !allowed.includes(kind))
41
+ throw new Error(outwardRefusal(kind));
42
+ }
43
+ }
32
44
  export function assertNotDestructive(action, innerArgs, inspected) {
33
45
  if (action !== ActionType.CLICK && action !== ActionType.DBLCLICK)
34
46
  return;
47
+ assertOutwardAllowed(inspected);
35
48
  if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
36
49
  return;
37
50
  if (!isDestructiveDescriptor(inspected))
@@ -44,6 +57,7 @@ export function assertNotDestructive(action, innerArgs, inspected) {
44
57
  * option dragged onto Save is not a payment.
45
58
  */
46
59
  export function assertDragNotDestructive(innerArgs, from, to) {
60
+ assertOutwardAllowed(from, to);
47
61
  if (true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
48
62
  return;
49
63
  if (!isDestructiveDescriptor(from) && !isDestructiveDescriptor(to))
@@ -67,3 +81,26 @@ export function assertNativeInputSupported(innerArgs) {
67
81
  if (true === innerArgs[NATIVE_INPUT_ARG])
68
82
  throw new Error(NATIVE_INPUT_UNSUPPORTED);
69
83
  }
84
+ /**
85
+ * The agent's own declaration counts as the confirmation, for that one control.
86
+ *
87
+ * `reticle_act_and_wait { target: { role: "button", name: "Place order" }, until }` names the
88
+ * control by the very words the guard flags AND names the consequence before acting: asking again
89
+ * for `confirmDangerous` cost a round trip per step on a shop's ordinary path and protected nothing.
90
+ * A `ref` or `testid` does not count — neither says what the control does, and a stale ref is
91
+ * exactly how the wrong button gets pressed. A Harness call never takes this path: its outward
92
+ * guard decides alone.
93
+ */
94
+ export function confirmedByDeclaration(innerArgs, target, declared) {
95
+ if (!declared || true === innerArgs[DANGEROUS_ACTION_CONFIRM_ARG])
96
+ return innerArgs;
97
+ if (currentDrivenBy() !== undefined)
98
+ return innerArgs;
99
+ const named = asRecord(target);
100
+ // The words must themselves be the dangerous ones: naming "Continue" on a button whose form posts
101
+ // to /delete-account consents to nothing, and the guard still reads the whole control.
102
+ const words = ['name', 'text', 'label'].map((key) => asString(named[key]) ?? '').join(' ');
103
+ return isDangerousActionText(words, asString(named['role']))
104
+ ? { ...innerArgs, [DANGEROUS_ACTION_CONFIRM_ARG]: true }
105
+ : innerArgs;
106
+ }
@@ -10,6 +10,7 @@
10
10
  * used from the two points where observation can be lost: a write rejected at dispatch, and a wait
11
11
  * cut off mid-predicate.
12
12
  */
13
+ import { type ReticleEvent } from '@reticlehq/core';
13
14
  import type { Session } from '../../../portal/session/session.js';
14
15
  import { type SuccessorRegistry } from '../../../portal/session/session-successor.js';
15
16
  import type { EvalResult } from '@reticlehq/engine/question/predicate/predicate-eval.js';
@@ -39,4 +40,36 @@ interface FollowedOutcome {
39
40
  * `awaitDocumentSuccessor` refuses to guess — a wrong follow drives somebody else's app.
40
41
  */
41
42
  export declare function followLostObservation(input: FollowSuccessor): Promise<FollowedOutcome>;
43
+ /** Why a consequence that did not follow a click on a disabled control is not the app's failure. */
44
+ export declare const DISABLED_TARGET_NOTE: string;
45
+ /**
46
+ * The page reports `enabled: false` on an act whose target was disabled at dispatch. A missed
47
+ * consequence then says nothing about the app, so the verdict is undecided rather than "no".
48
+ */
49
+ export declare function disabledTargetNote(pass: boolean, actResult: unknown): string | undefined;
50
+ /**
51
+ * The requests of a failed check's window, as its record carries them: method, url, status and the
52
+ * start of the response body (redacted in the page before it was ever sent). Dev tooling is left out.
53
+ */
54
+ /**
55
+ * The requests a check keeps: on a miss, the window's calls that explain it; on a held network check,
56
+ * the request that answered it, so a share page can name "a POST to /api/…", not "a request was seen".
57
+ */
58
+ export declare function checkNet(verdict: {
59
+ pass: boolean;
60
+ evidence?: unknown;
61
+ }, events: readonly ReticleEvent[]): {
62
+ net?: {
63
+ method: string;
64
+ url: string;
65
+ status?: number;
66
+ body?: string;
67
+ }[];
68
+ };
69
+ export declare function windowNet(events: readonly ReticleEvent[]): {
70
+ method: string;
71
+ url: string;
72
+ status?: number;
73
+ body?: string;
74
+ }[];
42
75
  export {};
@@ -10,6 +10,7 @@
10
10
  * used from the two points where observation can be lost: a write rejected at dispatch, and a wait
11
11
  * cut off mid-predicate.
12
12
  */
13
+ import { EventType, asRecord, asString, isDevToolingUrl } from '@reticlehq/core';
13
14
  import { awaitDocumentSuccessor, } from '../../../portal/session/session-successor.js';
14
15
  /**
15
16
  * Follow the unique same-origin successor and re-ask there, or leave the verdict untouched.
@@ -34,3 +35,59 @@ export async function followLostObservation(input) {
34
35
  return { session: next, verdict, followed: true };
35
36
  return { session: next, verdict: await reevaluate(next, leftover), followed: true };
36
37
  }
38
+ /** Why a consequence that did not follow a click on a disabled control is not the app's failure. */
39
+ export const DISABLED_TARGET_NOTE = 'the control was disabled when it was acted on, so nothing a user could do there would have ' +
40
+ 'caused the declared consequence. Check why it was disabled (an empty required field, a save ' +
41
+ 'that already happened) before calling this a defect.';
42
+ /**
43
+ * The page reports `enabled: false` on an act whose target was disabled at dispatch. A missed
44
+ * consequence then says nothing about the app, so the verdict is undecided rather than "no".
45
+ */
46
+ export function disabledTargetNote(pass, actResult) {
47
+ if (pass)
48
+ return undefined;
49
+ return false === asRecord(actResult)['enabled'] ? DISABLED_TARGET_NOTE : undefined;
50
+ }
51
+ /** Calls quoted on a failed check, and how much of each response body. */
52
+ const MAX_NET_CALLS = 5;
53
+ const MAX_BODY_CHARS = 300;
54
+ /**
55
+ * The requests of a failed check's window, as its record carries them: method, url, status and the
56
+ * start of the response body (redacted in the page before it was ever sent). Dev tooling is left out.
57
+ */
58
+ /**
59
+ * The requests a check keeps: on a miss, the window's calls that explain it; on a held network check,
60
+ * the request that answered it, so a share page can name "a POST to /api/…", not "a request was seen".
61
+ */
62
+ export function checkNet(verdict, events) {
63
+ if (!verdict.pass)
64
+ return { net: windowNet(events) };
65
+ const seen = asRecord(verdict.evidence);
66
+ const method = asString(seen['method']);
67
+ const url = asString(seen['url']);
68
+ if (method === undefined || url === undefined)
69
+ return {};
70
+ const status = seen['status'];
71
+ return { net: [{ method, url, ...('number' === typeof status ? { status } : {}) }] };
72
+ }
73
+ export function windowNet(events) {
74
+ const out = [];
75
+ for (const event of events) {
76
+ if (EventType.NET_REQUEST !== event.type)
77
+ continue;
78
+ const url = asString(event.data['url']);
79
+ if (url === undefined || isDevToolingUrl(url))
80
+ continue;
81
+ const status = event.data['status'];
82
+ const body = asString(event.data['responseBody']);
83
+ out.push({
84
+ method: (asString(event.data['method']) ?? 'GET').toUpperCase(),
85
+ url,
86
+ ...('number' === typeof status ? { status } : {}),
87
+ ...(body === undefined ? {} : { body: body.slice(0, MAX_BODY_CHARS) }),
88
+ });
89
+ if (MAX_NET_CALLS <= out.length)
90
+ break;
91
+ }
92
+ return out;
93
+ }
@@ -11,7 +11,7 @@
11
11
  * outcome than "unknown".
12
12
  */
13
13
  import { PredicateSchema } from '@reticlehq/engine/question/predicate/predicate-schema.js';
14
- import { isGlobalPressCall } from '@reticlehq/core';
14
+ import { SESSION_REF, isGlobalPressCall, unissuedRefRefusal } from '@reticlehq/core';
15
15
  import { SessionReplacedError } from '../../../portal/session/page-commands/pending-commands.js';
16
16
  import { assertNativeInputSupported } from './act-danger.js';
17
17
  import { unevaluablePredicateReason, vacuousPredicateReason, } from '@reticlehq/engine/question/predicate/predicate-precheck.js';
@@ -80,6 +80,19 @@ export function sequenceStepArgs(step) {
80
80
  flat[key] = value;
81
81
  }
82
82
  const nested = step['args'];
83
+ if ('string' === typeof nested) {
84
+ let parsed;
85
+ try {
86
+ parsed = JSON.parse(nested);
87
+ }
88
+ catch {
89
+ throw new Error(`step.args is a string that is not valid JSON — pass an object, not a string. Got: ${nested.slice(0, 120)}`);
90
+ }
91
+ if (null === parsed || 'object' !== typeof parsed || Array.isArray(parsed)) {
92
+ throw new Error(`step.args is a JSON string but not an object — pass an object, not ${null === parsed ? 'null' : typeof parsed}. Got: ${nested.slice(0, 120)}`);
93
+ }
94
+ return { ...flat, ...parsed };
95
+ }
83
96
  return { ...flat, ...(null !== nested && 'object' === typeof nested ? nested : {}) };
84
97
  }
85
98
  export function assertSequenceSteps(steps) {
@@ -113,8 +126,18 @@ export function assertSequenceSteps(steps) {
113
126
  'Refused rather than dropped, because a step whose declaration was discarded reports ' +
114
127
  'as a step that declared nothing. Nothing was acted on.');
115
128
  }
116
- if ('string' === typeof step['ref'] && step['ref'].length > 0)
129
+ if ('string' === typeof step['args']) {
130
+ sequenceStepArgs(step);
131
+ }
132
+ if ('string' === typeof step['ref'] && step['ref'].length > 0) {
133
+ // The same shape check `resolveActTarget` makes, moved ahead of the first step: left there, a
134
+ // malformed ref in step three is only found after steps one and two have already acted.
135
+ if (!SESSION_REF.test(step['ref'])) {
136
+ throw new Error(`step ${String(i)}: ${unissuedRefRefusal(step['ref'])} ` +
137
+ 'Nothing was acted on — the whole sequence is refused so a bad step cannot leave the earlier ones half-applied.');
138
+ }
117
139
  return;
140
+ }
118
141
  if (step['target'] !== undefined)
119
142
  return;
120
143
  if (isGlobalPressCall(step))
@@ -7,7 +7,7 @@
7
7
  * dispatch out of the file that opens the window would have weakened a real invariant to satisfy a
8
8
  * line count. This resolves a TARGET and dispatches only QUERY, so that guard is untouched.
9
9
  */
10
- import { ReticleCommand, isGlobalPressCall } from '@reticlehq/core';
10
+ import { ReticleCommand, SESSION_REF, isGlobalPressCall, unissuedRefRefusal, } from '@reticlehq/core';
11
11
  import { normalizeQueryArgs } from '../read/query-shape.js';
12
12
  import { resolveTargetRef } from './resolve-target.js';
13
13
  import { asRecord, asString, asNumber } from '@reticlehq/core';
@@ -49,8 +49,13 @@ export async function resolveActTarget(session, args,
49
49
  */
50
50
  appearanceMs) {
51
51
  const ref = asString(args['ref']);
52
- if (ref !== undefined && ref.length > 0)
52
+ if (ref !== undefined && ref.length > 0) {
53
+ // Refused here, at the boundary, and never re-read as a `target`: guessing what an arbitrary
54
+ // string meant is the silent re-resolution the stale-ref refusal exists to prevent.
55
+ if (!SESSION_REF.test(ref))
56
+ return { kind: 'error', message: unissuedRefRefusal(ref) };
53
57
  return { kind: 'ref', ref };
58
+ }
54
59
  const target = args['target'];
55
60
  if (target === undefined) {
56
61
  if (isGlobalPressCall(args))
@@ -94,7 +99,7 @@ appearanceMs) {
94
99
  // One match, or an ambiguity: both are answered by the existing resolver, which already ranks
95
100
  // candidates inside its refusal so the next turn needs no snapshot.
96
101
  if ('wait' !== decision.do)
97
- return resolveTargetRef(found);
102
+ return resolveTargetRef(found, asRecord(out.result)['hint']);
98
103
  await new Promise((resolve) => setTimeout(resolve, decision.waitMs));
99
104
  }
100
105
  }
@@ -29,4 +29,4 @@ export type TargetResolution = {
29
29
  * When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
30
30
  * agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
31
31
  */
32
- export declare function resolveTargetRef(candidates: readonly unknown[]): TargetResolution;
32
+ export declare function resolveTargetRef(candidates: readonly unknown[], hint?: unknown): TargetResolution;
@@ -61,6 +61,21 @@ function formatCandidate(c, anyInViewport) {
61
61
  const head = name.length > 0 ? `${ref} (${role} "${name}"` : `${ref} (${role}`;
62
62
  return `${head}; ${where})`;
63
63
  }
64
+ /**
65
+ * The names that role really has, when an exact role + name query just missed (the browser's
66
+ * `nameNearMiss` hint). Names are matched exactly, so one that carries a changing part — "billing
67
+ * Last run 1 minute ago" — is unreachable by name; `testid` or a `text` match is the durable way.
68
+ */
69
+ function nearNamesClause(hint) {
70
+ const near = asRecord(hint)['nameNearMiss'];
71
+ const names = Array.isArray(near) ? near.filter((n) => 'string' === typeof n) : [];
72
+ if (0 === names.length) {
73
+ return 'widen the query, or take a reticle_snapshot to see what is actually on the page.';
74
+ }
75
+ return (`names are matched exactly, and that role is called ${names.map((n) => `"${n}"`).join(', ')}. ` +
76
+ 'Use that name, or — when the name carries a part that changes, like a time — target it by ' +
77
+ '`testid` or by `text` (a contained match).');
78
+ }
64
79
  /**
65
80
  * Turn a target QUERY into the single ref an action can be performed on.
66
81
  *
@@ -81,15 +96,15 @@ function formatCandidate(c, anyInViewport) {
81
96
  * When several remain, they are ranked (in-viewport, then role) inside the refusal only — so the
82
97
  * agent can pass the top ref on the next turn without a snapshot. Ranking is never acted on here.
83
98
  */
84
- export function resolveTargetRef(candidates) {
99
+ export function resolveTargetRef(candidates, hint) {
85
100
  const all = candidates.map((c) => asRecord(c));
86
101
  const visible = all.filter((c) => c.visible !== false);
87
102
  const usable = visible.length > 0 ? visible : all;
88
103
  if (0 === usable.length) {
89
104
  return {
90
105
  kind: 'error',
91
- message: 'target matched no element. Nothing was acted on and no verdict is possible — widen the ' +
92
- 'query, or take a reticle_snapshot to see what is actually on the page.',
106
+ message: 'target matched no element. Nothing was acted on and no verdict is possible — ' +
107
+ nearNamesClause(hint),
93
108
  };
94
109
  }
95
110
  if (usable.length > 1) {