@reticlehq/server 3.7.0 → 3.8.1

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 (578) hide show
  1. package/SKILL.md +59 -68
  2. package/bin/reticle.js +9 -2
  3. package/dist/command/cli/adhoc-suite.js +1 -1
  4. package/dist/command/cli/adhoc-verdict.d.ts +12 -0
  5. package/dist/command/cli/adhoc-verdict.js +62 -4
  6. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  7. package/dist/command/cli/answers/gate-hook-message.js +5 -1
  8. package/dist/command/cli/auth/cloud-session.d.ts +11 -19
  9. package/dist/command/cli/auth/cloud-session.js +23 -0
  10. package/dist/command/cli/auth/device-flow.d.ts +3 -37
  11. package/dist/command/cli/cli-doctor.js +66 -21
  12. package/dist/command/cli/cli-flow-commands.js +53 -11
  13. package/dist/command/cli/cli-kill.d.ts +8 -0
  14. package/dist/command/cli/cli-kill.js +43 -3
  15. package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
  16. package/dist/command/cli/cli-parse-feedback.js +13 -1
  17. package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
  18. package/dist/command/cli/cli-parse-grammar.js +1 -6
  19. package/dist/command/cli/cli-parse-serve.d.ts +24 -0
  20. package/dist/command/cli/cli-parse-serve.js +124 -0
  21. package/dist/command/cli/cli-parse.d.ts +12 -11
  22. package/dist/command/cli/cli-parse.js +100 -123
  23. package/dist/command/cli/cli-usage.d.ts +0 -11
  24. package/dist/command/cli/cli-usage.js +43 -16
  25. package/dist/command/cli/cli-verify.d.ts +1 -1
  26. package/dist/command/cli/cli-verify.js +29 -14
  27. package/dist/command/cli/cloud-cli.js +27 -9
  28. package/dist/command/cli/cloud-kit.js +3 -3
  29. package/dist/command/cli/cloud-link.d.ts +1 -20
  30. package/dist/command/cli/cloud-link.js +2 -0
  31. package/dist/command/cli/cloud-login.d.ts +6 -2
  32. package/dist/command/cli/cloud-login.js +13 -41
  33. package/dist/command/cli/config/checkout.d.ts +10 -0
  34. package/dist/command/cli/config/checkout.js +78 -0
  35. package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
  36. package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
  37. package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
  38. package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
  39. package/dist/command/cli/doctor/doctor-rows.js +4 -0
  40. package/dist/command/cli/drive/drive-attach.d.ts +5 -0
  41. package/dist/command/cli/drive/drive-attach.js +21 -1
  42. package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
  43. package/dist/command/cli/hook/codex-transcript.js +224 -0
  44. package/dist/command/cli/hook/episode-structure.d.ts +24 -0
  45. package/dist/command/cli/hook/episode-structure.js +138 -0
  46. package/dist/command/cli/hook/hook-command.d.ts +9 -0
  47. package/dist/command/cli/hook/hook-command.js +211 -0
  48. package/dist/command/cli/hook/hook-fast.d.ts +31 -0
  49. package/dist/command/cli/hook/hook-fast.js +136 -0
  50. package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
  51. package/dist/command/cli/hook/prompt-hook.js +199 -0
  52. package/dist/command/cli/hook/tool-hook.d.ts +21 -0
  53. package/dist/command/cli/hook/tool-hook.js +259 -0
  54. package/dist/command/cli/launch/cli-launch.d.ts +2 -0
  55. package/dist/command/cli/launch/cli-launch.js +22 -2
  56. package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
  57. package/dist/command/cli/mcp-command.d.ts +9 -0
  58. package/dist/command/cli/mcp-command.js +127 -41
  59. package/dist/command/cli/open-navigate.d.ts +43 -0
  60. package/dist/command/cli/open-navigate.js +85 -0
  61. package/dist/command/cli/ports/port-holder.d.ts +16 -4
  62. package/dist/command/cli/ports/port-holder.js +58 -12
  63. package/dist/command/cli/ports/resolve/cli-port.js +18 -2
  64. package/dist/command/cli/project-name.d.ts +1 -7
  65. package/dist/command/cli/project-name.js +1 -0
  66. package/dist/command/cli/report-command.d.ts +8 -2
  67. package/dist/command/cli/report-command.js +95 -18
  68. package/dist/command/cli/status-fields.d.ts +9 -2
  69. package/dist/command/cli/status-fields.js +148 -4
  70. package/dist/command/cli/try-command.d.ts +3 -4
  71. package/dist/command/cli/try-command.js +6 -11
  72. package/dist/command/cli/verify-results.d.ts +4 -1
  73. package/dist/command/cli/verify-results.js +3 -1
  74. package/dist/command/cli.d.ts +1 -7
  75. package/dist/command/cli.js +79 -160
  76. package/dist/command/daemon/daemon-resilience.d.ts +6 -1
  77. package/dist/command/daemon/daemon-resilience.js +31 -18
  78. package/dist/command/daemon/daemon.d.ts +11 -0
  79. package/dist/command/daemon/daemon.js +70 -2
  80. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
  81. package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
  82. package/dist/command/daemon/wake-decision.js +4 -2
  83. package/dist/command/daemon/win32-escape.d.ts +32 -0
  84. package/dist/command/daemon/win32-escape.js +119 -0
  85. package/dist/command/setup/agent-configs.d.ts +9 -0
  86. package/dist/command/setup/agent-configs.js +9 -0
  87. package/dist/command/setup/auto-approve.d.ts +10 -3
  88. package/dist/command/setup/auto-approve.js +51 -3
  89. package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
  90. package/dist/command/setup/bringup/dev-server-wait.js +5 -1
  91. package/dist/command/setup/bringup/ensure-daemon.js +4 -7
  92. package/dist/command/setup/init/init-host.js +6 -0
  93. package/dist/command/setup/init/init-port.js +5 -4
  94. package/dist/command/setup/init/init-runtime.js +7 -1
  95. package/dist/command/setup/node-effects.d.ts +8 -0
  96. package/dist/command/setup/node-effects.js +36 -4
  97. package/dist/command/setup/probe/dev-port.d.ts +26 -0
  98. package/dist/command/setup/probe/dev-port.js +117 -0
  99. package/dist/command/setup/probe/page-probe.d.ts +1 -1
  100. package/dist/command/setup/probe/page-probe.js +11 -3
  101. package/dist/command/setup/remaining-steps.d.ts +0 -7
  102. package/dist/command/setup/remaining-steps.js +3 -2
  103. package/dist/command/setup/run-setup.d.ts +11 -0
  104. package/dist/command/setup/run-setup.js +33 -5
  105. package/dist/command/setup/setup-command.d.ts +2 -0
  106. package/dist/command/setup/setup-command.js +22 -4
  107. package/dist/command/update/updater.d.ts +1 -6
  108. package/dist/command/update/updater.js +4 -5
  109. package/dist/command/update/version-seen.d.ts +24 -0
  110. package/dist/command/update/version-seen.js +65 -0
  111. package/dist/command/version/peer-announce.d.ts +9 -1
  112. package/dist/command/version/peer-announce.js +25 -2
  113. package/dist/command/version/sdk-fix.d.ts +5 -6
  114. package/dist/command/version/sdk-fix.js +17 -32
  115. package/dist/command/version/version-skew.d.ts +16 -1
  116. package/dist/command/version/version-skew.js +84 -10
  117. package/dist/features/crawl/crawl-tools.js +2 -2
  118. package/dist/features/crawl/crawl.js +2 -0
  119. package/dist/features/exhaust/ledger.d.ts +15 -96
  120. package/dist/features/exhaust/ledger.js +3 -3
  121. package/dist/features/harness/drive-runs.d.ts +28 -4
  122. package/dist/features/harness/drive-runs.js +99 -7
  123. package/dist/features/harness/drivers.d.ts +1 -1
  124. package/dist/features/harness/drivers.js +1 -1
  125. package/dist/features/harness/harness.d.ts +5 -0
  126. package/dist/features/harness/harness.js +24 -12
  127. package/dist/features/harness/platform/platform-drives.d.ts +7 -1
  128. package/dist/features/harness/platform/platform-drives.js +12 -7
  129. package/dist/features/harness/platform/refusal.d.ts +1 -1
  130. package/dist/features/harness/platform/refusal.js +4 -4
  131. package/dist/features/harness/platform/remote-drive.js +70 -28
  132. package/dist/features/harness/platform/server-driver.js +22 -1
  133. package/dist/features/harness/platform-config.d.ts +1 -10
  134. package/dist/features/harness/platform-config.js +0 -20
  135. package/dist/features/license/license.d.ts +2 -14
  136. package/dist/features/visual/step-shots.d.ts +33 -1
  137. package/dist/features/visual/step-shots.js +155 -1
  138. package/dist/features/visual/visual-tools.js +12 -4
  139. package/dist/hooks/coding-agents.d.ts +6 -0
  140. package/dist/hooks/coding-agents.js +24 -3
  141. package/dist/hooks/episode-stamp.d.ts +32 -0
  142. package/dist/hooks/episode-stamp.js +61 -0
  143. package/dist/hooks/hook-commands.js +19 -9
  144. package/dist/index.d.ts +5 -1
  145. package/dist/index.js +66 -24
  146. package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
  147. package/dist/judgement/app-graph/journal-steps.js +92 -0
  148. package/dist/judgement/capsule/capsule-store.d.ts +2 -39
  149. package/dist/judgement/domain/domain-model.js +1 -10
  150. package/dist/judgement/domain/domain-tools.js +4 -2
  151. package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
  152. package/dist/judgement/runs/artifact/run-store.js +153 -28
  153. package/dist/judgement/runs/default-run-id.d.ts +3 -1
  154. package/dist/judgement/runs/default-run-id.js +15 -3
  155. package/dist/judgement/runs/drive-run.d.ts +3 -3
  156. package/dist/judgement/runs/drive-run.js +11 -7
  157. package/dist/judgement/runs/run-ledger.d.ts +21 -0
  158. package/dist/judgement/runs/run-ledger.js +143 -0
  159. package/dist/judgement/runs/runner-port.d.ts +1 -1
  160. package/dist/judgement/runs/runner-port.js +8 -2
  161. package/dist/judgement/runs/verification-sync.d.ts +4 -3
  162. package/dist/judgement/runs/verification-sync.js +27 -5
  163. package/dist/language/flows/affected-tools.js +2 -1
  164. package/dist/language/flows/annotate-notes/annotate.js +8 -3
  165. package/dist/language/flows/annotate-tools.js +1 -1
  166. package/dist/language/flows/change/affected.d.ts +8 -0
  167. package/dist/language/flows/change/affected.js +20 -7
  168. package/dist/language/flows/decision.js +19 -4
  169. package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
  170. package/dist/language/flows/fields/flow-secret-field.js +67 -5
  171. package/dist/language/flows/flow-anchor.d.ts +5 -0
  172. package/dist/language/flows/flow-anchor.js +4 -0
  173. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  174. package/dist/language/flows/flow-cross-step.js +9 -0
  175. package/dist/language/flows/flow-expect-grammar.js +15 -10
  176. package/dist/language/flows/flow-intent.js +6 -1
  177. package/dist/language/flows/flow-journey.d.ts +8 -0
  178. package/dist/language/flows/flow-journey.js +13 -0
  179. package/dist/language/flows/flow-lease.d.ts +27 -0
  180. package/dist/language/flows/flow-lease.js +59 -0
  181. package/dist/language/flows/flow-replay-run.d.ts +1 -87
  182. package/dist/language/flows/flow-replay-run.js +111 -310
  183. package/dist/language/flows/flow-replay.js +21 -19
  184. package/dist/language/flows/flow-replayable.d.ts +38 -0
  185. package/dist/language/flows/flow-replayable.js +179 -0
  186. package/dist/language/flows/flow-start-path.d.ts +108 -0
  187. package/dist/language/flows/flow-start-path.js +313 -0
  188. package/dist/language/flows/flow-step-runners.d.ts +23 -2
  189. package/dist/language/flows/flow-step-runners.js +191 -32
  190. package/dist/language/flows/flow-store-for-session.d.ts +8 -1
  191. package/dist/language/flows/flow-store-for-session.js +6 -0
  192. package/dist/language/flows/flow-tools.d.ts +2 -23
  193. package/dist/language/flows/flow-tools.js +63 -113
  194. package/dist/language/flows/flows.d.ts +4 -6
  195. package/dist/language/flows/flows.js +90 -15
  196. package/dist/language/flows/heal-run.d.ts +10 -4
  197. package/dist/language/flows/heal-run.js +26 -9
  198. package/dist/language/flows/recording/flake.d.ts +3 -27
  199. package/dist/language/flows/recording/flake.js +1 -1
  200. package/dist/language/flows/recording/recording-backtrack.js +1 -4
  201. package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
  202. package/dist/language/flows/recording/tape/recordings.js +9 -3
  203. package/dist/language/flows/replay.d.ts +25 -5
  204. package/dist/language/flows/replay.js +83 -16
  205. package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
  206. package/dist/language/flows/suite/coverage.d.ts +9 -0
  207. package/dist/language/flows/suite/coverage.js +12 -1
  208. package/dist/language/flows/suite-selection.d.ts +25 -0
  209. package/dist/language/flows/suite-selection.js +41 -9
  210. package/dist/machine/git-tracks.d.ts +6 -0
  211. package/dist/machine/git-tracks.js +36 -0
  212. package/dist/memory/cloud/background-sync.d.ts +25 -0
  213. package/dist/memory/cloud/background-sync.js +79 -0
  214. package/dist/memory/cloud/cloud-config.d.ts +12 -1
  215. package/dist/memory/cloud/cloud-config.js +39 -13
  216. package/dist/memory/cloud/cloud-sync.d.ts +3 -24
  217. package/dist/memory/cloud/cloud-sync.js +5 -2
  218. package/dist/memory/cloud/harness-config.d.ts +0 -7
  219. package/dist/memory/cloud/memory-scope.d.ts +19 -0
  220. package/dist/memory/cloud/memory-scope.js +48 -0
  221. package/dist/memory/cloud/sync-answer.d.ts +43 -0
  222. package/dist/memory/cloud/sync-answer.js +42 -0
  223. package/dist/memory/cloud/sync-cycle.d.ts +40 -10
  224. package/dist/memory/cloud/sync-cycle.js +97 -144
  225. package/dist/memory/cloud/sync-daemon.d.ts +50 -3
  226. package/dist/memory/cloud/sync-daemon.js +473 -83
  227. package/dist/memory/cloud/sync-describe.d.ts +28 -0
  228. package/dist/memory/cloud/sync-describe.js +92 -0
  229. package/dist/memory/cloud/sync-disk.d.ts +9 -1
  230. package/dist/memory/cloud/sync-disk.js +137 -33
  231. package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
  232. package/dist/memory/cloud/sync-flow-versions.js +42 -0
  233. package/dist/memory/impact/impact-store.js +32 -15
  234. package/dist/memory/intent/intent-shard.d.ts +41 -266
  235. package/dist/memory/intent/intent-tools.js +20 -61
  236. package/dist/memory/intent/record-request.d.ts +27 -0
  237. package/dist/memory/intent/record-request.js +88 -0
  238. package/dist/memory/intent/settled-words.js +9 -1
  239. package/dist/memory/journal/drive-flow.d.ts +3 -0
  240. package/dist/memory/journal/drive-flow.js +11 -41
  241. package/dist/memory/journal/envelope-store.js +1 -1
  242. package/dist/memory/journal/envelope.d.ts +6 -154
  243. package/dist/memory/journal/episode-log.d.ts +107 -0
  244. package/dist/memory/journal/episode-log.js +302 -0
  245. package/dist/memory/journal/journal-recorder.d.ts +7 -0
  246. package/dist/memory/journal/journal-recorder.js +22 -9
  247. package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
  248. package/dist/memory/journal/on-disk/ambient-file.js +1 -1
  249. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
  250. package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
  251. package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
  252. package/dist/memory/journal/self-observation.js +3 -1
  253. package/dist/memory/journal/session-end.js +23 -9
  254. package/dist/memory/journal/session-journal.js +39 -20
  255. package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
  256. package/dist/memory/project/artifact-root-resolver.js +68 -23
  257. package/dist/memory/project/artifact-root.d.ts +17 -3
  258. package/dist/memory/project/artifact-root.js +47 -9
  259. package/dist/memory/project/fs/fs-port.d.ts +5 -0
  260. package/dist/memory/project/fs/fs-port.js +13 -0
  261. package/dist/memory/project/learned-routes.d.ts +3 -4
  262. package/dist/memory/project/learned-routes.js +3 -11
  263. package/dist/memory/project/link-directory.js +5 -2
  264. package/dist/memory/project/root-claims.d.ts +3 -1
  265. package/dist/memory/project/root-claims.js +41 -7
  266. package/dist/memory/project/session-root.d.ts +1 -1
  267. package/dist/memory/project/session-root.js +1 -15
  268. package/dist/memory/project/sync-status.js +5 -6
  269. package/dist/memory/recall/memory-tools.js +2 -2
  270. package/dist/memory/recall/platform-memory.d.ts +3 -1
  271. package/dist/memory/recall/platform-memory.js +9 -1
  272. package/dist/memory/recall/project-memory.js +15 -19
  273. package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
  274. package/dist/portal/bridge/auth-failure-reason.js +19 -0
  275. package/dist/portal/bridge/bridge.d.ts +19 -4
  276. package/dist/portal/bridge/bridge.js +150 -177
  277. package/dist/portal/bridge/harness-request.d.ts +15 -0
  278. package/dist/portal/bridge/harness-request.js +19 -0
  279. package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
  280. package/dist/portal/bridge/origin/origin-follow.js +110 -0
  281. package/dist/portal/bridge/pairing-token.d.ts +6 -0
  282. package/dist/portal/bridge/pairing-token.js +47 -16
  283. package/dist/portal/bridge/protocol-skew.d.ts +27 -2
  284. package/dist/portal/bridge/protocol-skew.js +66 -2
  285. package/dist/portal/bridge/session-nonces.d.ts +46 -0
  286. package/dist/portal/bridge/session-nonces.js +72 -0
  287. package/dist/portal/input/viewport-tools.d.ts +31 -0
  288. package/dist/portal/input/viewport-tools.js +106 -15
  289. package/dist/portal/pool/bounded-close.d.ts +23 -0
  290. package/dist/portal/pool/bounded-close.js +38 -0
  291. package/dist/portal/pool/browser-pool.d.ts +44 -39
  292. package/dist/portal/pool/browser-pool.js +247 -95
  293. package/dist/portal/pool/context-permissions.d.ts +9 -1
  294. package/dist/portal/pool/context-permissions.js +26 -0
  295. package/dist/portal/pool/lease-emulation.d.ts +37 -0
  296. package/dist/portal/pool/lease-emulation.js +29 -0
  297. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  298. package/dist/portal/pool/playwright-launcher.js +74 -7
  299. package/dist/portal/pool/pool-contract.d.ts +23 -1
  300. package/dist/portal/pool/pool-lease.d.ts +128 -0
  301. package/dist/portal/pool/pool-lease.js +49 -0
  302. package/dist/portal/pool/zero-install.d.ts +8 -1
  303. package/dist/portal/pool/zero-install.js +24 -2
  304. package/dist/portal/production/production-activation.d.ts +136 -0
  305. package/dist/portal/production/production-activation.js +173 -0
  306. package/dist/portal/production/production-drive.d.ts +52 -0
  307. package/dist/portal/production/production-drive.js +301 -0
  308. package/dist/portal/production/production-hello.d.ts +23 -0
  309. package/dist/portal/production/production-hello.js +23 -0
  310. package/dist/portal/realm/web-realm.js +42 -15
  311. package/dist/portal/session/control-envelope.d.ts +1 -1
  312. package/dist/portal/session/dev-server/served-document.d.ts +0 -5
  313. package/dist/portal/session/dev-server/served-document.js +6 -8
  314. package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
  315. package/dist/portal/session/first-run-wiring.d.ts +3 -36
  316. package/dist/portal/session/first-run-wiring.js +10 -103
  317. package/dist/portal/session/live-control-tools.js +61 -19
  318. package/dist/portal/session/no-session-next-action.d.ts +11 -0
  319. package/dist/portal/session/no-session-next-action.js +40 -15
  320. package/dist/portal/session/no-session-watch.d.ts +7 -0
  321. package/dist/portal/session/no-session-watch.js +67 -23
  322. package/dist/portal/session/output-budget.d.ts +10 -0
  323. package/dist/portal/session/output-budget.js +16 -0
  324. package/dist/portal/session/presence/session-lease.d.ts +2 -0
  325. package/dist/portal/session/presence/session-lease.js +8 -0
  326. package/dist/portal/session/recorded-gaps.d.ts +15 -11
  327. package/dist/portal/session/recorded-gaps.js +39 -18
  328. package/dist/portal/session/serving-directory.d.ts +24 -2
  329. package/dist/portal/session/serving-directory.js +93 -5
  330. package/dist/portal/session/session-info.d.ts +26 -0
  331. package/dist/portal/session/session-info.js +20 -0
  332. package/dist/portal/session/session-manager.d.ts +35 -3
  333. package/dist/portal/session/session-manager.js +69 -14
  334. package/dist/portal/session/session-successor.js +1 -9
  335. package/dist/portal/session/session-tools.js +22 -4
  336. package/dist/portal/session/session.d.ts +10 -10
  337. package/dist/portal/session/session.js +37 -29
  338. package/dist/portal/session/zero-install-first-call.d.ts +7 -2
  339. package/dist/portal/session/zero-install-first-call.js +43 -20
  340. package/dist/start-wiring.d.ts +2 -2
  341. package/dist/start-wiring.js +17 -3
  342. package/dist/status-payload.d.ts +11 -1
  343. package/dist/status-payload.js +7 -1
  344. package/dist/surface/agent-presence-grace.d.ts +11 -0
  345. package/dist/surface/agent-presence-grace.js +34 -0
  346. package/dist/surface/http-server.d.ts +12 -1
  347. package/dist/surface/http-server.js +64 -15
  348. package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
  349. package/dist/surface/mcp/faults/mcp-outage.js +19 -1
  350. package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
  351. package/dist/surface/mcp/mcp-proxy.js +194 -267
  352. package/dist/surface/mcp/mcp.d.ts +20 -3
  353. package/dist/surface/mcp/mcp.js +88 -25
  354. package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
  355. package/dist/surface/mcp/proxy/drive-calls.js +114 -5
  356. package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
  357. package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
  358. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
  359. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
  360. package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
  361. package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
  362. package/dist/surface/mcp/server-instructions.js +35 -16
  363. package/dist/surface/mcp/shared-params.d.ts +8 -0
  364. package/dist/surface/mcp/shared-params.js +15 -1
  365. package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
  366. package/dist/surface/mcp/sse-frame-parser.js +18 -0
  367. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  368. package/dist/surface/mcp/surface-vocabulary.js +13 -0
  369. package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
  370. package/dist/surface/mcp/tool-catalog-cache.js +14 -4
  371. package/dist/surface/tools/account-tool.d.ts +4 -3
  372. package/dist/surface/tools/account-tool.js +20 -31
  373. package/dist/surface/tools/act/act-danger.d.ts +2 -0
  374. package/dist/surface/tools/act/act-danger.js +41 -14
  375. package/dist/surface/tools/act/act-observation.d.ts +11 -0
  376. package/dist/surface/tools/act/act-observation.js +33 -0
  377. package/dist/surface/tools/act/act-preflight.js +10 -3
  378. package/dist/surface/tools/act/act-sequence-retry.js +2 -1
  379. package/dist/surface/tools/act/act-target.js +19 -5
  380. package/dist/surface/tools/act/durable.js +1 -1
  381. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
  382. package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
  383. package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
  384. package/dist/surface/tools/act/unsettled-find.js +71 -0
  385. package/dist/surface/tools/act-sequence-tool.js +53 -24
  386. package/dist/surface/tools/act-tools.d.ts +2 -3
  387. package/dist/surface/tools/act-tools.js +49 -47
  388. package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
  389. package/dist/surface/tools/agent/for-the-agent.js +41 -3
  390. package/dist/surface/tools/assert/assert-grade.js +8 -3
  391. package/dist/surface/tools/assert/assert-verdict.js +4 -2
  392. package/dist/surface/tools/browser-tools.js +7 -4
  393. package/dist/surface/tools/chat-drives.js +50 -25
  394. package/dist/surface/tools/coverage-tools.js +1 -1
  395. package/dist/surface/tools/dynamic-tools.d.ts +8 -0
  396. package/dist/surface/tools/dynamic-tools.js +89 -34
  397. package/dist/surface/tools/empty-session-list.d.ts +18 -25
  398. package/dist/surface/tools/empty-session-list.js +67 -38
  399. package/dist/surface/tools/error-recovery.d.ts +17 -7
  400. package/dist/surface/tools/error-recovery.js +53 -20
  401. package/dist/surface/tools/explore-tools.d.ts +10 -0
  402. package/dist/surface/tools/explore-tools.js +72 -19
  403. package/dist/surface/tools/feedback-tools.js +23 -2
  404. package/dist/surface/tools/gap-novelty.d.ts +6 -0
  405. package/dist/surface/tools/gap-novelty.js +9 -0
  406. package/dist/surface/tools/gaps/capability-absences.js +4 -3
  407. package/dist/surface/tools/harness-explore.d.ts +1 -1
  408. package/dist/surface/tools/harness-explore.js +6 -4
  409. package/dist/surface/tools/harness-gate.d.ts +43 -21
  410. package/dist/surface/tools/harness-gate.js +55 -81
  411. package/dist/surface/tools/harness-script.d.ts +2 -0
  412. package/dist/surface/tools/harness-script.js +11 -6
  413. package/dist/surface/tools/harness-toolset.js +3 -4
  414. package/dist/surface/tools/invoke-tool.d.ts +0 -8
  415. package/dist/surface/tools/invoke-tool.js +164 -178
  416. package/dist/surface/tools/lease-hint.d.ts +9 -0
  417. package/dist/surface/tools/lease-hint.js +22 -3
  418. package/dist/surface/tools/lease-permissions.d.ts +1 -1
  419. package/dist/surface/tools/lease-readiness.d.ts +55 -0
  420. package/dist/surface/tools/lease-readiness.js +150 -2
  421. package/dist/surface/tools/lease-tools.d.ts +3 -75
  422. package/dist/surface/tools/lease-tools.js +124 -188
  423. package/dist/surface/tools/map-tools.d.ts +20 -0
  424. package/dist/surface/tools/map-tools.js +145 -0
  425. package/dist/surface/tools/merge-tools.js +11 -7
  426. package/dist/surface/tools/next-step.js +6 -3
  427. package/dist/surface/tools/observe-tools.js +46 -35
  428. package/dist/surface/tools/platform-moment.d.ts +3 -12
  429. package/dist/surface/tools/platform-moment.js +12 -24
  430. package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
  431. package/dist/surface/tools/read/complete-snapshot.js +13 -2
  432. package/dist/surface/tools/read/query-paginate.js +6 -0
  433. package/dist/surface/tools/read/query-shape.d.ts +9 -0
  434. package/dist/surface/tools/read/query-shape.js +15 -0
  435. package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
  436. package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
  437. package/dist/surface/tools/read-tools.js +5 -5
  438. package/dist/surface/tools/real-input-attempt.d.ts +11 -0
  439. package/dist/surface/tools/real-input-attempt.js +206 -38
  440. package/dist/surface/tools/reconcile-tools.js +21 -8
  441. package/dist/surface/tools/reticle-result.d.ts +3 -0
  442. package/dist/surface/tools/reticle-result.js +8 -1
  443. package/dist/surface/tools/run-report.d.ts +22 -0
  444. package/dist/surface/tools/run-report.js +141 -0
  445. package/dist/surface/tools/session-ports.d.ts +57 -0
  446. package/dist/surface/tools/session-ports.js +102 -0
  447. package/dist/surface/tools/tool-kit.d.ts +58 -4
  448. package/dist/surface/tools/tool-kit.js +140 -6
  449. package/dist/surface/tools/tool-surface.d.ts +2 -0
  450. package/dist/surface/tools/tool-surface.js +45 -0
  451. package/dist/surface/tools/tools.js +49 -87
  452. package/dist/telemetry/automation-hint.d.ts +5 -0
  453. package/dist/telemetry/automation-hint.js +7 -0
  454. package/dist/telemetry/cli-telemetry.d.ts +22 -0
  455. package/dist/telemetry/cli-telemetry.js +80 -11
  456. package/dist/telemetry/daemon-telemetry.d.ts +3 -1
  457. package/dist/telemetry/daemon-telemetry.js +9 -2
  458. package/dist/telemetry/feedback-cli.d.ts +1 -1
  459. package/dist/telemetry/feedback-cli.js +8 -3
  460. package/dist/telemetry/feedback.d.ts +1 -1
  461. package/dist/telemetry/feedback.js +51 -4
  462. package/dist/telemetry/harness-run.d.ts +3 -3
  463. package/dist/telemetry/harness-run.js +18 -7
  464. package/dist/telemetry/hud-events.d.ts +1 -1
  465. package/dist/telemetry/hud-metrics.d.ts +6 -3
  466. package/dist/telemetry/hud-metrics.js +20 -8
  467. package/dist/telemetry/install-source.js +1 -1
  468. package/dist/telemetry/mcp-connection.d.ts +1 -1
  469. package/dist/telemetry/mcp-connection.js +21 -2
  470. package/dist/telemetry/onboarding-firsts.d.ts +6 -1
  471. package/dist/telemetry/onboarding-firsts.js +41 -1
  472. package/dist/telemetry/proxy-telemetry.d.ts +42 -0
  473. package/dist/telemetry/proxy-telemetry.js +70 -1
  474. package/dist/telemetry/session-metrics.d.ts +1 -3
  475. package/dist/telemetry/session-metrics.js +16 -12
  476. package/dist/telemetry/telemetry.d.ts +9 -1
  477. package/dist/telemetry/telemetry.js +16 -1
  478. package/dist/telemetry/verification-of.d.ts +3 -1
  479. package/dist/telemetry/verification-of.js +7 -1
  480. package/dist/wire-journal.js +12 -2
  481. package/docs/README.md +4 -2
  482. package/docs/actions.mdx +7 -6
  483. package/docs/adapters.md +2 -1
  484. package/docs/agent-cheatsheet.md +19 -5
  485. package/docs/architecture.md +13 -10
  486. package/docs/autodrive.md +6 -5
  487. package/docs/benchmarks.md +2 -2
  488. package/docs/capabilities.mdx +1 -1
  489. package/docs/cli/affected.mdx +1 -1
  490. package/docs/cli/cloud.mdx +6 -6
  491. package/docs/cli/doctor.mdx +3 -1
  492. package/docs/cli/feedback.mdx +11 -7
  493. package/docs/cli/gate.mdx +1 -1
  494. package/docs/cli/help.mdx +1 -1
  495. package/docs/cli/init.mdx +20 -16
  496. package/docs/cli/mcp.mdx +5 -2
  497. package/docs/cli/open.mdx +5 -2
  498. package/docs/cli/report.mdx +1 -1
  499. package/docs/cli/restart.mdx +1 -1
  500. package/docs/cli/rollback.mdx +4 -4
  501. package/docs/cli/serve.mdx +7 -5
  502. package/docs/cli/setup.mdx +1 -1
  503. package/docs/cli/status.mdx +6 -2
  504. package/docs/cli/stop.mdx +3 -3
  505. package/docs/cli/telemetry.mdx +5 -1
  506. package/docs/cli/try.mdx +8 -8
  507. package/docs/cli/update.mdx +8 -5
  508. package/docs/cli/verify.mdx +12 -5
  509. package/docs/cli/version.mdx +1 -1
  510. package/docs/cli.mdx +10 -6
  511. package/docs/debugging.md +1 -1
  512. package/docs/deploy-checks.md +8 -6
  513. package/docs/desktop.mdx +14 -18
  514. package/docs/enterprise.md +6 -6
  515. package/docs/environment.mdx +5 -3
  516. package/docs/faq.mdx +9 -6
  517. package/docs/first-drive.md +19 -9
  518. package/docs/flows.md +3 -4
  519. package/docs/for-agents.md +1 -1
  520. package/docs/frameworks.mdx +34 -11
  521. package/docs/getting-started.md +46 -29
  522. package/docs/http-transport.md +2 -2
  523. package/docs/human-control.md +8 -8
  524. package/docs/index.mdx +2 -2
  525. package/docs/install-agentic.mdx +18 -19
  526. package/docs/install-manual.mdx +8 -4
  527. package/docs/integration-patterns.md +5 -5
  528. package/docs/multi-agent-testing.md +7 -5
  529. package/docs/packages/browser.mdx +34 -2
  530. package/docs/packages/core.mdx +2 -1
  531. package/docs/packages/electron.mdx +15 -3
  532. package/docs/packages/eslint-plugin.mdx +1 -1
  533. package/docs/packages/next.mdx +17 -5
  534. package/docs/packages/react.mdx +8 -1
  535. package/docs/packages/server.mdx +3 -3
  536. package/docs/packages/tauri.mdx +3 -3
  537. package/docs/packages/vite-plugin.mdx +1 -1
  538. package/docs/packages.mdx +19 -6
  539. package/docs/platform-integration.md +12 -4
  540. package/docs/predicates.mdx +18 -7
  541. package/docs/production-mode.md +108 -0
  542. package/docs/prompt-capture.md +99 -0
  543. package/docs/quickstart.mdx +14 -12
  544. package/docs/recipes.mdx +9 -8
  545. package/docs/required-checks.md +3 -2
  546. package/docs/skill-file.mdx +4 -3
  547. package/docs/skill-setup.md +2 -2
  548. package/docs/skill.md +62 -70
  549. package/docs/state-files.md +1 -0
  550. package/docs/state-management.mdx +11 -9
  551. package/docs/system-map.md +14 -13
  552. package/docs/telemetry.md +32 -24
  553. package/docs/testing.md +9 -5
  554. package/docs/tools/act-and-wait.mdx +17 -13
  555. package/docs/tools/act-sequence.mdx +40 -66
  556. package/docs/tools/act.mdx +7 -5
  557. package/docs/tools/assert.mdx +1 -1
  558. package/docs/tools/look.mdx +1 -1
  559. package/docs/tools/navigate.mdx +1 -1
  560. package/docs/tools/network.mdx +13 -11
  561. package/docs/tools/overview.mdx +3 -3
  562. package/docs/tools/query.mdx +3 -3
  563. package/docs/tools/session-and-feedback.mdx +9 -6
  564. package/docs/tools/sessions.mdx +7 -0
  565. package/docs/tools/snapshot.mdx +9 -7
  566. package/docs/tools/tools-and-run.mdx +11 -10
  567. package/docs/tools/verify.mdx +1 -1
  568. package/docs/troubleshooting.mdx +21 -9
  569. package/docs/usage.md +58 -43
  570. package/docs/use-cases.mdx +2 -2
  571. package/docs/vs-chrome-devtools-mcp.mdx +5 -5
  572. package/docs/vs-playwright-mcp.mdx +5 -4
  573. package/docs/what-is-recorded.md +12 -7
  574. package/docs/what-is-reticle.mdx +2 -2
  575. package/docs/why-reticle.mdx +1 -1
  576. package/package.json +9 -12
  577. package/dist/command/dev/stale-issue-guard.d.ts +0 -34
  578. package/dist/command/dev/stale-issue-guard.js +0 -69
@@ -4,19 +4,34 @@
4
4
  * An empty list is the most common thing that tool returns, and on its own it is a dead end: the
5
5
  * agent asked the one question it knows to ask and got no next step. So it carries why nothing is
6
6
  * connected, the executable next action, the last tab that was here, and, on a project that has
7
- * never connected, the first-run wiring itself (see first-run-wiring.ts).
7
+ * never connected, the next step: the zero-install lease, and the consent question for `init`.
8
+ *
9
+ * This call changes no file. It used to run `init` in an unwired project: an agent that only
10
+ * listed sessions changed package.json, the lockfile and the build config, and started a second
11
+ * dev server, which the user did not ask for. Wiring is the user's decision.
8
12
  */
9
13
  import { dirname } from 'node:path';
10
14
  import { z } from 'zod';
11
- import { DiscoveryInvite, NoSessionAction } from '@reticlehq/core';
15
+ import { DiscoveryInvite, NextStepOffered, NoSessionAction } from '@reticlehq/core';
12
16
  import { readProjectId } from '../../command/cli/ports/resolve/cli-port.js';
13
17
  import { callingClientDirectory } from '../../hooks/client-directory.js';
14
- /** The answers that mean "this app is not set up yet", which a first run can settle by wiring it. */
18
+ import { initCommandIn } from '../../portal/session/dev-server/dev-command.js';
19
+ import { wiringConsent, zeroInstallLease, } from '../../portal/session/no-session-next-action.js';
20
+ import { noteNextStepOffered } from '../../telemetry/onboarding-firsts.js';
21
+ // The handshake reads the consent wording from here, so `mcp` needs no reach into `session`.
22
+ export { wiringConsent };
23
+ /** The answers that mean "this app is not set up yet". */
15
24
  const FIRST_RUN_ACTIONS = new Set([
16
25
  NoSessionAction.RUN_INIT,
17
26
  NoSessionAction.START_DEV_SERVER,
18
27
  NoSessionAction.OPEN_APP,
19
28
  ]);
29
+ /** The answers where an app is up, so the lease can check it now. */
30
+ const LEASABLE_ACTIONS = new Set([
31
+ NoSessionAction.RUN_INIT,
32
+ NoSessionAction.OPEN_APP,
33
+ ]);
34
+ const LOCALHOST = 'http://localhost';
20
35
  /** The fields only an empty list carries, declared so a schema-aware client does not strip them. */
21
36
  export const EMPTY_SESSION_LIST_OUTPUT = {
22
37
  why: z
@@ -36,10 +51,14 @@ export const EMPTY_SESSION_LIST_OUTPUT = {
36
51
  .string()
37
52
  .optional()
38
53
  .describe('Present ONLY when `sessions` is empty: a call with the founder for the person you are working with. Offer it; never act on it.'),
39
- wired: z
40
- .record(z.unknown())
54
+ next_step: z
55
+ .object({
56
+ offered: z.nativeEnum(NextStepOffered),
57
+ lease: z.string().optional(),
58
+ consent: z.string(),
59
+ })
41
60
  .optional()
42
- .describe('Files the first run changed (`steps`); tell the user.'),
61
+ .describe('Present ONLY when this project is not wired. Nothing was installed and no file changed. `lease` is the call that checks the running app now, with nothing installed: do it first. `consent` is the question for wiring the app for good: ask the user, and run the command only if they agree.'),
43
62
  lastKnown: z
44
63
  .object({
45
64
  sessionId: z.string(),
@@ -49,63 +68,72 @@ export const EMPTY_SESSION_LIST_OUTPUT = {
49
68
  .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.'),
50
69
  };
51
70
  /**
52
- * Wire the calling project when another project's tab is the one currently connected.
71
+ * The next step for an unwired project, or undefined when the project is wired or cannot be told.
72
+ *
73
+ * The lease leads when an app is up: it checks the change now and installs nothing. When no app
74
+ * is up there is nothing to lease yet, so the consent question leads. Both name the consent
75
+ * question, because only the user can agree to the file changes that `init` makes.
76
+ */
77
+ function nextStepFor(deps, dir, next) {
78
+ if (next === undefined || !FIRST_RUN_ACTIONS.has(next.action)) {
79
+ return undefined;
80
+ }
81
+ const project = deps.firstRun?.projectDirectory(dir());
82
+ if (project === undefined) {
83
+ return undefined;
84
+ }
85
+ const consent = wiringConsent(initCommandIn(project));
86
+ const leasable = LEASABLE_ACTIONS.has(next.action) && next.port !== undefined;
87
+ const offered = leasable ? NextStepOffered.LEASE : NextStepOffered.INIT_CONSENT;
88
+ noteNextStepOffered(offered, () => readProjectId(project) !== undefined);
89
+ if (!leasable) {
90
+ return { offered, consent };
91
+ }
92
+ return { offered, lease: zeroInstallLease(`${LOCALHOST}:${String(next.port)}`), consent };
93
+ }
94
+ /**
95
+ * The next step for the calling project when another project's tab is the one connected.
53
96
  *
54
97
  * The list handler only used to reach {@link emptySessionList} when the daemon's whole list was
55
98
  * empty. A live tab in project A then answered project B's first call with A's sessions, and B
56
- * was never wired.
99
+ * never heard that it was not wired.
57
100
  */
58
- export async function wireCallerBesideLiveSessions(deps, sessions) {
101
+ export function nextStepBesideLiveSessions(deps, sessions) {
59
102
  const dir = callingClientDirectory();
60
- if (dir === undefined)
103
+ if (dir === undefined) {
61
104
  return undefined;
105
+ }
62
106
  const projectId = readProjectId(dir);
63
107
  if (projectId !== undefined && sessions.some((session) => session.projectId === projectId)) {
64
108
  return undefined;
65
109
  }
66
- const next = deps.sessions.noSessionNextAction();
67
- const target = next !== undefined && FIRST_RUN_ACTIONS.has(next.action)
68
- ? deps.firstRun?.projectDirectory(dir)
69
- : undefined;
70
- if (target === undefined || deps.firstRun === undefined)
71
- return undefined;
72
- return deps.firstRun.wire(target);
110
+ return nextStepFor(deps, () => dir, deps.sessions.noSessionNextAction());
73
111
  }
74
112
  /** The departed tab for this call: the caller's own project when it named one, else the daemon's newest. */
75
113
  function departedForCaller(deps) {
76
114
  const dir = callingClientDirectory();
77
- if (dir === undefined)
115
+ if (dir === undefined) {
78
116
  return deps.sessions.lastKnown?.();
117
+ }
79
118
  const projectId = readProjectId(dir);
80
- if (projectId === undefined)
119
+ if (projectId === undefined) {
81
120
  return undefined;
121
+ }
82
122
  return deps.sessions.lastKnownFor?.(projectId);
83
123
  }
84
- export async function emptySessionList(deps, sessions) {
124
+ export function emptySessionList(deps, sessions) {
85
125
  // The lead and its next action: the full differential (~3 KB of ranked causes) read as
86
126
  // speculation on the call an agent makes first. Optional-call: test stubs predate the lead.
87
127
  const why = deps.sessions.noSessionLead?.() ?? deps.sessions.noSessionHint();
88
128
  // The executable half. `why` is for the human reading the transcript; this is the one the
89
129
  // agent acts on, so it never has to parse a paragraph to find a command inside it.
90
130
  const next = deps.sessions.noSessionNextAction();
91
- // The first run wires the app the client is in, not the directory the daemon was started in.
92
- // One daemon serves every project; the proxy names its cwd on the call. With no caller (an
93
- // embed, a test) it is still the daemon's own project. A daemon outside any project asks instead.
94
- // ponytail: cwd, not an MCP roots/list round trip. A proxy started outside the project still
95
- // answers run_init; roots are the upgrade when a client launches there but names the project.
96
- // Not only on `run_init`: an unwired app whose dev server is not running yet answers
97
- // `start_dev_server`, which is the commonest first run of all, and init starts that server itself.
98
- // `projectDirectory` refuses a project that is already wired, so a wired app is never re-run.
99
- const dir = next !== undefined && FIRST_RUN_ACTIONS.has(next.action)
100
- ? deps.firstRun?.projectDirectory(callingClientDirectory() ?? dirname(deps.reticleRoot))
101
- : undefined;
102
- if (dir !== undefined && deps.firstRun !== undefined) {
103
- const wired = await deps.firstRun.wire(dir);
104
- const connected = deps.sessions.list();
105
- if (wired.ok && 0 < connected.length)
106
- return { sessions: connected, wired };
107
- return { sessions, wired, ...(why === undefined ? {} : { why }) };
108
- }
131
+ // The project the client is in, not the directory the daemon was started in. One daemon serves
132
+ // every project; the proxy names its cwd on the call. With no caller (an embed, a test) it is the
133
+ // daemon's own project. A daemon outside any project offers no step.
134
+ // ponytail: cwd, not an MCP roots/list round trip. Roots are the upgrade when a client launches
135
+ // outside the project but names it.
136
+ const nextStep = nextStepFor(deps, () => callingClientDirectory() ?? dirname(deps.reticleRoot), next);
109
137
  // The last tab, when one was here. Optional-call: test stubs of SessionManager predate it.
110
138
  const known = departedForCaller(deps);
111
139
  const lastKnown = undefined === known ? undefined : { sessionId: known.id, url: known.url };
@@ -113,6 +141,7 @@ export async function emptySessionList(deps, sessions) {
113
141
  sessions,
114
142
  ...(why === undefined ? {} : { why }),
115
143
  ...(next === undefined ? {} : { next_action: next }),
144
+ ...(nextStep === undefined ? {} : { next_step: nextStep }),
116
145
  ...(undefined === lastKnown ? {} : { lastKnown }),
117
146
  // Setup that never connects is where most people give up; it is the talk worth having.
118
147
  talk_to_us: DiscoveryInvite.AGENT,
@@ -8,7 +8,7 @@
8
8
  * on stable, human-authored substrings of the thrown messages (see session-manager.ts, the flow/
9
9
  * baseline stores). No clock, no IO — unit-testable in isolation.
10
10
  */
11
- import { RefusalReason } from '@reticlehq/core';
11
+ import { RefusalReason, type CodedRefusal } from '@reticlehq/core';
12
12
  /** The recovery hints, named so they are not free strings and can be asserted in tests. */
13
13
  export declare const RECOVERY: {
14
14
  readonly NO_SESSION: string;
@@ -41,6 +41,7 @@ export declare const RECOVERY: {
41
41
  */
42
42
  readonly AMBIGUOUS_TARGET: string;
43
43
  readonly NO_SUCH_OPTION: string;
44
+ readonly PRODUCTION_POLICY: string;
44
45
  readonly BAD_ARGUMENTS: string;
45
46
  readonly WRONG_TARGET: string;
46
47
  readonly NOT_EDITABLE: string;
@@ -54,9 +55,10 @@ export declare const RECOVERY: {
54
55
  * something that is not a top-level parameter is the whole distance between a one-line retry and
55
56
  * a lost budget.
56
57
  *
57
- * The classifier is also wider than "destructive": it matches `deploy` and `publish`, so an
58
- * ordinary "New deploy" button trips it. Naming a real trigger word here keeps that from reading
59
- * as a malfunction on an app where nothing is being deleted.
58
+ * The classifier also stops a control that moves money, which is not "destructive". Naming the
59
+ * real trigger words here keeps a refusal on a "Pay" button from reading as a malfunction. Only
60
+ * words the classifier matches are named: `deploy` and `publish` are excluded on purpose (see
61
+ * `core/src/verdict/security.ts`).
60
62
  */
61
63
  readonly CONFIRM_DANGEROUS: string;
62
64
  readonly UNSUPPORTED_SURFACE: string;
@@ -85,7 +87,6 @@ export declare const RECOVERY: {
85
87
  * agent's malformed call: it was then told to fix arguments that were never wrong.
86
88
  */
87
89
  readonly HARNESS_LOCKED: string;
88
- readonly HARNESS_NOT_LINKED: string;
89
90
  };
90
91
  /**
91
92
  * The actionable next move for a known error message, or undefined when none is recognized.
@@ -104,7 +105,7 @@ export declare function recoveryFor(message: string): string | undefined;
104
105
  * is the bucket that says the classifier has a blind spot — and a growing `other` is a finding about
105
106
  * this table rather than about anybody's app.
106
107
  */
107
- export declare function refusalReasonFor(rawMessage: string): RefusalReason;
108
+ export declare function refusalReasonFor(rawMessage: string, coded?: CodedRefusal): RefusalReason;
108
109
  /**
109
110
  * The ask attached to errors we do NOT recognize. An unrecognized error is, by definition, the case
110
111
  * where Reticle failed in a way nobody anticipated — the single highest-value moment to hear from the
@@ -120,11 +121,20 @@ interface ErrorPayload {
120
121
  error: string;
121
122
  recovery?: string;
122
123
  feedback?: string;
124
+ /** A coded refusal: what it is, and who does what next, as data. */
125
+ code?: CodedRefusal['code'];
126
+ next?: CodedRefusal['next'];
123
127
  }
128
+ /**
129
+ * The code and next steps a thrown refusal carries (`HarnessNotLinkedError`), or undefined. Read from
130
+ * the error itself, so a refusal is recognized by its code and never by a regular expression over
131
+ * its sentence: the not-linked refusal was matched by its prose, and the prose was rewritten.
132
+ */
133
+ export declare function codedRefusalOf(error: unknown): CodedRefusal | undefined;
124
134
  /**
125
135
  * Build the tool-error payload spliced at the MCP boundary. `recovery` is added when the error is
126
136
  * known; the feedback ask is added when it is NOT — the two are mutually exclusive by design, so the
127
137
  * agent always gets exactly one next move.
128
138
  */
129
- export declare function buildErrorPayload(rawMessage: string): ErrorPayload;
139
+ export declare function buildErrorPayload(rawMessage: string, coded?: CodedRefusal): ErrorPayload;
130
140
  export {};
@@ -8,10 +8,12 @@
8
8
  * on stable, human-authored substrings of the thrown messages (see session-manager.ts, the flow/
9
9
  * baseline stores). No clock, no IO — unit-testable in isolation.
10
10
  */
11
- import { RefusalReason, ReticleEnv, TRANSPORT_LIMITS, UNISSUED_REF_REFUSAL } from '@reticlehq/core';
11
+ import { CodedRefusalSchema, PRODUCTION_REFUSAL_PREFIX, RefusalCode, RefusalReason, ReticleEnv, TRANSPORT_LIMITS, UNISSUED_REF_REFUSAL, } from '@reticlehq/core';
12
12
  import { SELF_RECOVERING_MARKER } from '../../portal/session/no-session-diagnosis.js';
13
13
  import { MSG_HARNESS_DISABLED, MSG_HARNESS_UNCLAIMED, MSG_HARNESS_UNCONFIRMED, PlatformRefusalCode, RefusalLead, } from '../../features/harness/platform/refusal.js';
14
14
  import { BODY_CLAUSE_REFUSAL_OPENING } from '@reticlehq/engine/evidence/body-capture-remedy.js';
15
+ import { EMPTY_FIND_OPENING } from './read/query-shape.js';
16
+ import { UNKNOWN_FLOW_OPENING } from '../../language/flows/suite-selection.js';
15
17
  import { CHROMIUM_PATH_REFUSAL_PREFIX, chromiumInstallCommand, bundledPlaywrightVersion, } from '../../command/cli/doctor/browser/chromium-hint.js';
16
18
  /** Marks where an over-long message had its middle removed. Named so tests assert it by value. */
17
19
  const ELISION = '… [elided] …';
@@ -64,6 +66,10 @@ const SELF_RECOVERING = [
64
66
  // was collecting the defect ask on top of its own remedy, so the envelope contradicted itself and
65
67
  // asked for a bug report about somebody's connect() options.
66
68
  { match: literally(BODY_CLAUSE_REFUSAL_OPENING), reason: RefusalReason.UNSUPPORTED },
69
+ // A find with nothing to find names the fields to pass; an unknown flow name lists the saved ones.
70
+ // Both are the caller's to fix, and the defect ask on top told an agent to file a bug about them.
71
+ { match: literally(EMPTY_FIND_OPENING), reason: RefusalReason.BAD_ARGS },
72
+ { match: literally(UNKNOWN_FLOW_OPENING), reason: RefusalReason.NO_MATCH },
67
73
  ];
68
74
  /**
69
75
  * The refusal for a `ref` Reticle never minted — decided BEFORE every other rule, not by one inside
@@ -158,6 +164,11 @@ export const RECOVERY = {
158
164
  'and the change event that follows has been observed persisting an empty setting into the app. ' +
159
165
  'Retry with one of the listed values — note they are option VALUES, which often differ from the ' +
160
166
  'visible labels shown in brackets. This is an invalid call, not a Reticle defect.',
167
+ PRODUCTION_POLICY: 'The production policy refused this act before it reached the site, and nothing was done. The ' +
168
+ 'call itself was valid. Do not retry it: the policy refuses it every time. Leave this step out, ' +
169
+ 'or, when the message names an intent, end the lease and acquire a new one whose ' +
170
+ '`production.intent` names the action. This is a refusal by design, not a Reticle defect: there ' +
171
+ 'is nothing to report.',
161
172
  BAD_ARGUMENTS: "That call did not match the tool's schema — the message above names the tool and the exact " +
162
173
  "argument it wanted. Re-read that tool's parameters and retry with the missing or corrected " +
163
174
  'argument. This is an invalid call, not a Reticle defect: there is nothing to report.',
@@ -181,12 +192,13 @@ export const RECOVERY = {
181
192
  * something that is not a top-level parameter is the whole distance between a one-line retry and
182
193
  * a lost budget.
183
194
  *
184
- * The classifier is also wider than "destructive": it matches `deploy` and `publish`, so an
185
- * ordinary "New deploy" button trips it. Naming a real trigger word here keeps that from reading
186
- * as a malfunction on an app where nothing is being deleted.
195
+ * The classifier also stops a control that moves money, which is not "destructive". Naming the
196
+ * real trigger words here keeps a refusal on a "Pay" button from reading as a malfunction. Only
197
+ * words the classifier matches are named: `deploy` and `publish` are excluded on purpose (see
198
+ * `core/src/verdict/security.ts`).
187
199
  */
188
- CONFIRM_DANGEROUS: 'Reticle blocked a control whose label reads as consequential (delete, remove, revoke, deploy, ' +
189
- 'publish, pay…) on purpose. If you mean it, send the SAME call again with ' +
200
+ CONFIRM_DANGEROUS: 'Reticle blocked a control whose label reads as consequential (delete, remove, revoke, pay…) ' +
201
+ 'on purpose. If you mean it, send the SAME call again with ' +
190
202
  '`args: { confirmDangerous: true }` — it goes inside the `args` object, not beside it. This is ' +
191
203
  'a deliberate refusal, not a defect: there is nothing to report.',
192
204
  UNSUPPORTED_SURFACE: 'That surface is not supported yet, and Reticle refuses rather than pretending — a rich-text ' +
@@ -260,13 +272,9 @@ export const RECOVERY = {
260
272
  * the refusal names reticle_act_and_wait as the alternative, which the catch-all below read as an
261
273
  * agent's malformed call: it was then told to fix arguments that were never wrong.
262
274
  */
263
- HARNESS_LOCKED: 'The Harness unlocks once Reticle sees enough of this app; the refusal names what is missing ' +
264
- 'and how to close each gap. Nothing to report: close them, or drive the journey yourself with ' +
265
- 'reticle_act_and_wait and an `until` on its last step. Every other tool works at any coverage.',
266
- HARNESS_NOT_LINKED: 'This project is not linked to the Reticle platform, where the Harness runs. The call itself was ' +
267
- 'valid. Either the human runs `npx @reticlehq/server connect` once in the app directory, or you ' +
268
- 'drive the journey yourself: reticle_navigate, then reticle_act_and_wait with an `until` on its ' +
269
- 'last step. There is nothing to report.',
275
+ HARNESS_LOCKED: 'An older Reticle platform locked the Harness on its setup score. Nothing to report: drive the ' +
276
+ 'journey yourself with reticle_act_and_wait and an `until` on its last step. Every tool works ' +
277
+ 'at any coverage.',
270
278
  };
271
279
  /**
272
280
  * Which OWNER each recovery belongs to, for `tool_refused`.
@@ -305,12 +313,12 @@ const REASON_OF = {
305
313
  NOT_EDITABLE: RefusalReason.UNSUPPORTED,
306
314
  CONFIRM_DANGEROUS: RefusalReason.UNSUPPORTED,
307
315
  WRONG_TARGET: RefusalReason.UNSUPPORTED,
316
+ // The call was valid; the drive's policy refuses what it asked for, as for a destructive control.
317
+ PRODUCTION_POLICY: RefusalReason.UNSUPPORTED,
308
318
  BAD_ARGUMENTS: RefusalReason.BAD_ARGS,
309
319
  INVALID_NAME: RefusalReason.BAD_ARGS,
310
320
  // Nothing about the app or the call is wrong; the feature is switched off or unpaid for.
311
321
  HARNESS_OFF: RefusalReason.UNSUPPORTED,
312
- // Nothing about the call is wrong either; the project is not linked yet.
313
- HARNESS_NOT_LINKED: RefusalReason.UNSUPPORTED,
314
322
  // The call is valid; the app is not instrumented enough for the Harness yet.
315
323
  HARNESS_LOCKED: RefusalReason.UNSUPPORTED,
316
324
  };
@@ -349,11 +357,10 @@ const RULES = [
349
357
  // platform's: matching it is matching ourselves.
350
358
  { match: /credits are used\./, hint: RECOVERY.HARNESS_OFF },
351
359
  {
352
- // The gate's own words, and the platform's "locked: connect your app first": expected states.
353
- match: new RegExp(`(?:^Harness unlocks at \\d+% instrumentation)|(?:^${escapeRegExp(RefusalLead[PlatformRefusalCode.HARNESS_LOCKED])})`),
360
+ // An older platform's "locked": an expected state, not a defect.
361
+ match: new RegExp(`^${escapeRegExp(RefusalLead[PlatformRefusalCode.HARNESS_LOCKED])}`),
354
362
  hint: RECOVERY.HARNESS_LOCKED,
355
363
  },
356
- { match: /The Reticle Harness runs on the Reticle platform/, hint: RECOVERY.HARNESS_NOT_LINKED },
357
364
  // Three conditions the daemon understands perfectly and still asked for a bug report about. Each
358
365
  // needs its own rule: none of them contains "no browser session connected" (the scope miss is
359
366
  // "no browser session FOR project 'x'", which is the opposite claim — sessions exist).
@@ -412,6 +419,8 @@ const RULES = [
412
419
  match: /target matched \d+ elements and an action must not guess/i,
413
420
  hint: RECOVERY.AMBIGUOUS_TARGET,
414
421
  },
422
+ // Before the catch-all: every refusal names `reticle_lease`, and the call was valid.
423
+ { match: new RegExp(`^${PRODUCTION_REFUSAL_PREFIX}`), hint: RECOVERY.PRODUCTION_POLICY },
415
424
  // Authored by Reticle, about the caller's arguments: the message already names the valid answers.
416
425
  { match: /^unknown action '/i, hint: RECOVERY.BAD_ARGUMENTS },
417
426
  { match: /^unsupported query strategy '/i, hint: RECOVERY.BAD_ARGUMENTS },
@@ -473,7 +482,9 @@ export function recoveryFor(message) {
473
482
  * is the bucket that says the classifier has a blind spot — and a growing `other` is a finding about
474
483
  * this table rather than about anybody's app.
475
484
  */
476
- export function refusalReasonFor(rawMessage) {
485
+ export function refusalReasonFor(rawMessage, coded) {
486
+ if (coded !== undefined)
487
+ return REASON_OF_CODE[coded.code];
477
488
  const message = zodArrayAsSentence(rawMessage);
478
489
  if (isUnissuedRefRefusal(message))
479
490
  return RefusalReason.BAD_ARGS;
@@ -591,15 +602,37 @@ const INVALID_NAME_REJECTION = /^invalid (?:[a-z]+ )*name: /i;
591
602
  export const FEEDBACK_ASK = 'This error is not one Reticle recognizes, which means it may be a defect in Reticle rather than in ' +
592
603
  'the app. If you believe Reticle misbehaved, call reticle_session { action: "feedback" } with a root-cause analysis and ' +
593
604
  'the call trace before moving on — that report is the only way this gets fixed.';
605
+ /** Which owner each coded refusal belongs to, for `tool_refused`. */
606
+ const REASON_OF_CODE = {
607
+ // Nothing about the call is wrong; the project is not linked yet.
608
+ [RefusalCode.HARNESS_NOT_LINKED]: RefusalReason.UNSUPPORTED,
609
+ };
610
+ /**
611
+ * The code and next steps a thrown refusal carries (`HarnessNotLinkedError`), or undefined. Read from
612
+ * the error itself, so a refusal is recognized by its code and never by a regular expression over
613
+ * its sentence: the not-linked refusal was matched by its prose, and the prose was rewritten.
614
+ */
615
+ export function codedRefusalOf(error) {
616
+ if (!(error instanceof Error))
617
+ return undefined;
618
+ const parsed = CodedRefusalSchema.safeParse({
619
+ code: 'code' in error ? error.code : undefined,
620
+ next: 'next' in error ? error.next : undefined,
621
+ });
622
+ return parsed.success ? parsed.data : undefined;
623
+ }
594
624
  /**
595
625
  * Build the tool-error payload spliced at the MCP boundary. `recovery` is added when the error is
596
626
  * known; the feedback ask is added when it is NOT — the two are mutually exclusive by design, so the
597
627
  * agent always gets exactly one next move.
598
628
  */
599
- export function buildErrorPayload(rawMessage) {
629
+ export function buildErrorPayload(rawMessage, coded) {
600
630
  // Render the zod array BEFORE capping: the cap would otherwise truncate the JSON mid-issue and
601
631
  // leave an unparseable fragment as the agent's error text — the worst of both shapes.
602
632
  const message = capMessage(zodArrayAsSentence(rawMessage));
633
+ // A coded refusal says who does what next itself: no hint beside it, and no bug-report ask.
634
+ if (coded !== undefined)
635
+ return { error: message, code: coded.code, next: coded.next };
603
636
  if (isUnissuedRefRefusal(message))
604
637
  return { error: message, recovery: RECOVERY.UNISSUED_REF };
605
638
  // A self-diagnosing message gets NEITHER a generic recovery nor the feedback ask. It already
@@ -30,6 +30,16 @@ export type ExploreDrive = (deps: ToolDeps, env: Record<string, string | undefin
30
30
  * with the drive already running rather than paying for another.
31
31
  */
32
32
  export declare function answerExplore(deps: ToolDeps, args: Record<string, unknown>, call: ToolCall | undefined, drive: ExploreDrive): Promise<unknown>;
33
+ /**
34
+ * What a finished drive asks of the agent that reads it: a failed check is a defect to fix, and the
35
+ * fix is proved only when the same drive runs again. Without it the agent read "1 failed" and stopped.
36
+ */
37
+ export declare function fixRequest(result: Record<string, unknown>): string | undefined;
38
+ /**
39
+ * The platform's failed checks, each verbatim, read from its run summary. A plan run leaves the
40
+ * driver's account out of the answer, and with it went "totalCents 16700, the claim says 13360".
41
+ */
42
+ export declare function platformFindings(summary: string): string[];
33
43
  /** The tool's answer for a finished drive. */
34
44
  export declare function exploreReport(explored: ExploreResult): Record<string, unknown>;
35
45
  /**
@@ -10,6 +10,7 @@
10
10
  * Those flows are the point. From the second run onwards `reticle_verify { action: "flows" }` replays
11
11
  * them deterministically with no model in the loop at all.
12
12
  */
13
+ import { HarnessRunReason } from '@reticlehq/core/telemetry';
13
14
  import { z } from 'zod';
14
15
  import { randomUUID } from 'node:crypto';
15
16
  import { join } from 'node:path';
@@ -27,7 +28,7 @@ import { RESULT_FIELD, exploreResult, resultField } from './reticle-result.js';
27
28
  import { runTool } from './invoke-tool.js';
28
29
  import { harnessRunSource, reportHarnessBlocked, reportHarnessRun, } from '../../telemetry/harness-run.js';
29
30
  import { driveVerdict } from '../../features/harness/platform/remote-drive.js';
30
- import { harnessLockRefusal, firstLine, exploreApp, harnessAvailable, withLinkedCredential, MSG_NO_HARNESS_KEY, } from './harness-explore.js';
31
+ import { harnessStartRefusal, firstLine, exploreApp, harnessAvailable, withLinkedCredential, HarnessNotLinkedError, } from './harness-explore.js';
31
32
  import { EXPLORE_NEEDS } from '../../features/harness/drivers.js';
32
33
  import { checkTally, describeDrive, replayedFlows, runProved, } from '../../features/harness/drive-report.js';
33
34
  import { StopReason } from '../../features/harness/harness.js';
@@ -36,7 +37,7 @@ export const WAIT_MAX_S = 240;
36
37
  export const EXPLORE_TOOLS = [
37
38
  {
38
39
  name: ReticleTool.VERIFY_EXPLORE,
39
- description: 'Drive the app yourself? Do not — call this instead. The Reticle Harness drives it through this same tool surface (it decides on the Reticle platform, this daemon executes each step), records what it drove as saved flows, and answers in a few lines instead of a context full of snapshots: within `wait` seconds, the result or { status: "running", runId } to poll with `runId`. Never start a second. Pass `persona` to say who to be or what to accomplish ("a returning customer checking out", "an admin revoking a seat") and it completes that whole journey rather than clicking at random. The saved flows are the point: from the next run on, reticle_verify { action: "flows" } replays them deterministically with NO model in the loop. DESTRUCTIVE — it really drives the app, and it spends Harness credits. ' +
40
+ description: 'Drive the app yourself? Do not — call this instead. The Reticle Harness drives it through this same tool surface (it decides on the Reticle platform, this daemon executes each step), records what it drove as saved flows, and answers in a few lines instead of a context full of snapshots: within `wait` seconds, the result or { status: "running", runId } to poll with `runId`. Never start a second. Pass `persona` to say who to be or what to accomplish ("a returning customer checking out", "an admin revoking a seat") and it completes that whole journey rather than clicking at random. The saved flows are the point: from the next run on, reticle_verify { action: "flows" } replays them deterministically with NO model in the loop. DESTRUCTIVE — it really drives the app, and it spends credits. ' +
40
41
  EXPLORE_NEEDS,
41
42
  inputSchema: {
42
43
  persona: z
@@ -54,14 +55,15 @@ export const EXPLORE_TOOLS = [
54
55
  .string()
55
56
  .optional()
56
57
  .describe('Active session ID from reticle_sessions. Omit when only one browser session is open.'),
57
- driveId: z.string().optional(),
58
- // Bare on purpose: the description and every `running` answer say how to poll, and this
59
- // schema is re-sent on every turn of every session.
58
+ driveId: z.string().optional().describe('The drive id an explore call answered with.'),
60
59
  wait: z.number().min(0).max(WAIT_MAX_S).optional().describe('Seconds; default 50.'),
61
- runId: z.string().optional(),
60
+ runId: z
61
+ .string()
62
+ .optional()
63
+ .describe('The run id an explore call answered with; pass it to poll that run.'),
62
64
  planId: z.string().optional().describe('Run a plan saved on the dashboard, as it stands.'),
63
- stop: z.boolean().optional(),
64
- say: z.string().optional(),
65
+ stop: z.boolean().optional().describe('With runId: stop that Harness run.'),
66
+ say: z.string().optional().describe('With runId: a note to the running Harness.'),
65
67
  },
66
68
  // Everything the handler returns has to be declared, or a schema-aware client never sees it.
67
69
  // Optional throughout: a drive still running answers only its status, id and progress.
@@ -76,7 +78,7 @@ export const EXPLORE_TOOLS = [
76
78
  next: z.object({ action: z.string(), runId: z.string() }).optional(),
77
79
  /** Seconds since the drive started, while it runs. */
78
80
  elapsedS: z.number().optional(),
79
- /** While it runs: poll again with the same call; stopping never stops the drive. */
81
+ /** While it runs: poll again. Once it ended with a failed check: fix the code, then drive again. */
80
82
  instruction: z.string().optional(),
81
83
  stopReason: z.string().optional(),
82
84
  /** Which driver actually drove, so a comparison can never misattribute its own result. */
@@ -127,6 +129,11 @@ export const EXPLORE_TOOLS = [
127
129
  * at its end, which a caller that only shows the verdict otherwise cannot tell anybody.
128
130
  */
129
131
  driverAccount: z.string().optional(),
132
+ /**
133
+ * The platform's own failed checks, verbatim: what was expected, what was observed, on which
134
+ * request. The fix request quotes them, so the agent knows exactly what to fix.
135
+ */
136
+ findings: z.array(z.string()).optional(),
130
137
  /** Present when the drive broke: a model that would not answer, a wedged browser. */
131
138
  error: z.string().optional(),
132
139
  /** What the drive cost, cache hits included, so an expensive run is visible rather than felt. */
@@ -196,7 +203,7 @@ export async function answerExplore(deps, args, call, drive) {
196
203
  // Billed to the one free drive the platform granted, never to another.
197
204
  ...('string' === typeof driveId ? { [ReticleEnv.DRIVE_ID]: driveId } : {}),
198
205
  };
199
- // The coverage gate first: it is local, and the one thing to fix before anything else matters.
206
+ // The build check first: it is local, and the one thing to fix before anything else matters.
200
207
  // Not for a saved plan that only replays flows (no model turn to guard), and a plan it refuses
201
208
  // is told so, so the plan's history records the attempt.
202
209
  const planId = str(args['planId']);
@@ -213,17 +220,16 @@ export async function answerExplore(deps, args, call, drive) {
213
220
  source: harnessRunSource(origin, planId),
214
221
  hosted: platform !== undefined,
215
222
  };
216
- const locked = replayOnly ? undefined : await harnessLockRefusal(deps, sessionId, env);
217
- if (locked !== undefined)
218
- reportHarnessBlocked(runStart);
223
+ const locked = replayOnly ? undefined : await harnessStartRefusal(deps, sessionId);
219
224
  if (locked !== undefined) {
225
+ reportHarnessBlocked(runStart, locked.reason);
220
226
  if (planId !== undefined && platform !== undefined)
221
- await reportPlanBlocked(platform, planId, firstLine(locked));
222
- throw new Error(locked);
227
+ await reportPlanBlocked(platform, planId, firstLine(locked.text));
228
+ throw new Error(locked.text);
223
229
  }
224
230
  if (!harnessAvailable(env)) {
225
- reportHarnessBlocked(runStart);
226
- throw new Error(MSG_NO_HARNESS_KEY);
231
+ reportHarnessBlocked(runStart, HarnessRunReason.NOT_LINKED);
232
+ throw new HarnessNotLinkedError();
227
233
  }
228
234
  const harness = randomUUID();
229
235
  const runId = harnessRunId(harness);
@@ -260,12 +266,56 @@ async function answerDrive(deps, runId, waitMs, call, sessionId, refuseBroken) {
260
266
  instruction: stillRunning(runId, record.steps, elapsedS),
261
267
  };
262
268
  }
263
- if (record.result !== undefined)
264
- return { status: record.status, runId, ...record.result };
269
+ if (record.result !== undefined) {
270
+ const fix = fixRequest(record.result);
271
+ return {
272
+ status: record.status,
273
+ runId,
274
+ ...record.result,
275
+ ...(fix === undefined ? {} : { instruction: fix }),
276
+ };
277
+ }
265
278
  if (refuseBroken && record.error !== undefined)
266
279
  throw new Error(record.error);
267
280
  return { status: record.status, runId, steps: record.steps, error: record.error ?? 'no result' };
268
281
  }
282
+ /**
283
+ * What a finished drive asks of the agent that reads it: a failed check is a defect to fix, and the
284
+ * fix is proved only when the same drive runs again. Without it the agent read "1 failed" and stopped.
285
+ */
286
+ export function fixRequest(result) {
287
+ const failed = asRecord(result['checks'])['failed'];
288
+ const findings = Array.isArray(result['findings'])
289
+ ? result['findings'].filter((f) => 'string' === typeof f)
290
+ : [];
291
+ const rerun = 'Fix the code, then start the same drive again (the same persona or planId) to verify the fix. ' +
292
+ 'Do not report it fixed before that drive passes.';
293
+ if (0 < findings.length)
294
+ return `The Harness found a defect: ${findings.join(' ')} ${rerun}`;
295
+ if ('number' === typeof failed && failed >= 1)
296
+ return (`The Harness found a defect: ${String(failed)} check${1 === failed ? '' : 's'} failed. Read ` +
297
+ `\`summary\` and \`goals\` for what failed. ${rerun}`);
298
+ // A drive that ran to its end and still missed its goal is not a pass either: on the broken
299
+ // checkout every check held while the order went out at full price. It is a lead, not a verdict,
300
+ // so the agent is asked to find out which, not told there is a defect.
301
+ if (StopReason.FINISHED === result['stopReason'] && false === result['goalMet'])
302
+ return ('The Harness drive did not reach its goal, so nothing is proved. Read `summary` and check the ' +
303
+ 'outcome the goal names (for example the request body with reticle_observe). If it shows a ' +
304
+ 'defect, fix the code, then start the same drive again (the same persona or planId) to verify ' +
305
+ 'the fix. Do not report it fixed before that drive passes.');
306
+ return undefined;
307
+ }
308
+ /** How the platform opens a check it failed in a drive's summary: "Not proved: <what, where>". */
309
+ const PLATFORM_FAILED = /Not proved: ([^\n]+)/g;
310
+ /** How the answer's summary says one of them. */
311
+ const FINDING_LEAD = 'Failed on the platform:';
312
+ /**
313
+ * The platform's failed checks, each verbatim, read from its run summary. A plan run leaves the
314
+ * driver's account out of the answer, and with it went "totalCents 16700, the claim says 13360".
315
+ */
316
+ export function platformFindings(summary) {
317
+ return [...summary.matchAll(PLATFORM_FAILED)].flatMap((m) => m[1] === undefined ? [] : [m[1].trim()]);
318
+ }
269
319
  /** A run this machine no longer holds, from the platform it synced to. */
270
320
  async function fromPlatform(deps, runId) {
271
321
  const platform = serverOptionsFromEnv(await withLinkedCredential(deps, process.env));
@@ -354,8 +404,10 @@ const exploreDrive = async (deps, env, args, harness, control) => {
354
404
  export function exploreReport(explored) {
355
405
  const { drive, savedFlows, rewroteFlows, unverifiedFlows, driverName, plan, goals, planLines, runIds, runUrl, journeys, planId, planUrl, disparity, } = explored;
356
406
  const checks = checkTally(drive.toolCalls);
407
+ const findings = platformFindings(drive.summary);
357
408
  return {
358
409
  stopReason: drive.stopReason,
410
+ ...(0 === findings.length ? {} : { findings }),
359
411
  driver: driverName,
360
412
  steps: drive.steps,
361
413
  savedFlows: [...savedFlows],
@@ -395,6 +447,7 @@ export function exploreReport(explored) {
395
447
  ...(planLines ?? []),
396
448
  describeDrive(drive.toolCalls, [...savedFlows, ...rewroteFlows], unverifiedFlows, drive.goalMet, journeys),
397
449
  ...[unprovedGoals(goals)].filter((line) => line !== undefined),
450
+ ...findings.map((finding) => `${FINDING_LEAD} ${finding}`),
398
451
  ].join('\n'),
399
452
  // Not beside a platform plan: its lanes' accounts count saves the platform asked for ("saved 3
400
453
  // flow(s)") where `savedFlows` lists what is on disk, and an answer has one count. Each journey's