@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
@@ -37,3 +37,18 @@ export function normalizeQueryArgs(args) {
37
37
  }
38
38
  return args;
39
39
  }
40
+ /** How the refusal of an empty find opens: the error table recognizes it by these words. */
41
+ export const EMPTY_FIND_OPENING = 'Nothing was searched: this find names nothing to find.';
42
+ /**
43
+ * The error for a query that names nothing to find, or `undefined` when it names something.
44
+ *
45
+ * Sent to the page, an empty query answered `count: 0`, which reads as "nothing here matches" about
46
+ * a question nobody asked. A container asked for itself (`self`) needs no other field.
47
+ */
48
+ export function emptyQueryRefusal(args) {
49
+ if (true === args['self'] || 'string' === typeof args['by'] || 'string' === typeof args['value'])
50
+ return undefined;
51
+ return new Error(`${EMPTY_FIND_OPENING} Pass one of testid, text, role ` +
52
+ '(with name), label, placeholder or alt, e.g. { testid: "submit" } or { role: "button", ' +
53
+ 'name: "Save" }, or `by` with `value`.');
54
+ }
@@ -0,0 +1 @@
1
+ export declare function noteUnmountedRoot(result: unknown, mode: string, session?: unknown, scope?: string): unknown;
@@ -0,0 +1,73 @@
1
+ import { EventType, SnapshotMode, asNumber, asRecord, asString } from '@reticlehq/core';
2
+ /**
3
+ * How much DOM there is under the mount container, when the tree is empty and nothing was skipped.
4
+ *
5
+ * A handful of elements is a container with nothing in it. React's own root div counts as one, and a
6
+ * wrapper or two is ordinary, so this is deliberately not `=== 0`.
7
+ */
8
+ const UNMOUNTED_ROOT_MAX_ELEMENTS = 3;
9
+ function crashEvidence(session) {
10
+ const since = asRecord(session)['eventsSince'];
11
+ if ('function' !== typeof since)
12
+ return undefined;
13
+ let events;
14
+ try {
15
+ events = since.call(session, 0);
16
+ }
17
+ catch {
18
+ return undefined;
19
+ }
20
+ if (!Array.isArray(events))
21
+ return undefined;
22
+ const errors = events.filter((e) => {
23
+ const type = asString(asRecord(e)['type']);
24
+ return EventType.CONSOLE_ERROR === type || EventType.ERROR_UNCAUGHT === type;
25
+ });
26
+ if (0 === errors.length)
27
+ return undefined;
28
+ const first = errors
29
+ .map((e) => asString(asRecord(asRecord(e)['data'])['message']))
30
+ .find((m) => undefined !== m && '' !== m);
31
+ return { count: errors.length, first: first ?? '' };
32
+ }
33
+ export function noteUnmountedRoot(result, mode, session, scope) {
34
+ // A scope narrows the walk to one element, so a near-empty DOM under it says nothing about the app.
35
+ if (SnapshotMode.STATUS === mode || (scope ?? '') !== '')
36
+ return result;
37
+ const row = asRecord(result);
38
+ if (0 !== asNumber(row['nodes']))
39
+ return result;
40
+ if (row['note'] !== undefined)
41
+ return result;
42
+ const elements = asNumber(row['domElements']);
43
+ if (elements === undefined || elements > UNMOUNTED_ROOT_MAX_ELEMENTS)
44
+ return result;
45
+ const mount = `the tree is empty because there is almost nothing in the DOM: ${String(elements)} ` +
46
+ `element(s) under this scope. That is a mount container with nothing rendered into it, not a ` +
47
+ `page whose contents were skipped — so the app is UNMOUNTED rather than slow, and waiting ` +
48
+ `will not change it. `;
49
+ // An uncaught error beside an empty mount container is the difference between "has not started"
50
+ // and "started and died", which are the two readings of this count and want opposite next moves.
51
+ // Nothing here is framework-specific, and deliberately so: the condition is a known mount
52
+ // container that holds nothing with an uncaught error beside it, which is as true of a Vue or
53
+ // Svelte root as a React one.
54
+ const crash = crashEvidence(session);
55
+ if (crash !== undefined) {
56
+ return {
57
+ ...row,
58
+ note: mount +
59
+ `The app did not merely fail to start — it CRASHED: ${String(crash.count)} uncaught ` +
60
+ `error(s) were logged in this session` +
61
+ ('' === crash.first ? '' : `, the first: "${crash.first}"`) +
62
+ `. Read reticle_console for the rest, and check anything that runs inside a framework ` +
63
+ `commit phase. Loading the app again will reproduce it rather than fix it.`,
64
+ };
65
+ }
66
+ return {
67
+ ...row,
68
+ note: mount +
69
+ `An app that was rendering and then stopped has usually thrown: read ` +
70
+ `reticle_console for an uncaught error, and check anything that runs inside a framework ` +
71
+ `commit phase. If the app has genuinely not started yet, load it and snapshot again.`,
72
+ };
73
+ }
@@ -199,7 +199,7 @@ export const READ_TOOLS = [
199
199
  },
200
200
  outputSchema: {
201
201
  recordingName: z.string(),
202
- program: z.unknown(),
202
+ program: z.unknown().optional(),
203
203
  warning: z.string().optional(),
204
204
  proposedConsequences: z
205
205
  .array(z.unknown())
@@ -386,7 +386,7 @@ export const READ_TOOLS = [
386
386
  ...sessionIdShape,
387
387
  },
388
388
  outputSchema: {
389
- stores: z.record(z.unknown()).optional(),
389
+ stores: z.record(z.string(), z.unknown()).optional(),
390
390
  storeNames: z.array(z.string()).optional(),
391
391
  found: z.boolean().optional(),
392
392
  value: z.unknown().optional(),
@@ -598,9 +598,9 @@ export const READ_TOOLS = [
598
598
  ...sessionIdShape,
599
599
  },
600
600
  outputSchema: {
601
- local: z.record(z.string()).optional(),
602
- session: z.record(z.string()).optional(),
603
- cookies: z.record(z.string()).optional(),
601
+ local: z.record(z.string(), z.string()).optional(),
602
+ session: z.record(z.string(), z.string()).optional(),
603
+ cookies: z.record(z.string(), z.string()).optional(),
604
604
  area: z.string().optional(),
605
605
  key: z.string().optional(),
606
606
  value: z.string().optional(),
@@ -49,6 +49,8 @@ interface RealActResult {
49
49
  * rather than pretend the action ran.
50
50
  */
51
51
  export declare const HOVER_NEEDS_POINTER_MSG = "cannot hover without a real pointer \u2014 CSS :hover only applies to a native mouse move, never to a synthetic mouseover";
52
+ /** A hover target the page could not bring into the viewport. The pointer would miss it. */
53
+ export declare const HOVER_OFF_VIEWPORT_MSG = "cannot hover: the target is outside the viewport and could not be scrolled into view, so a real pointer would miss it";
52
54
  /**
53
55
  * Attempt to drive a pointer action via native input. Returns a synthetic outcome (with a
54
56
  * `reason` when a provider is configured) whenever the synthetic path should run — no matching
@@ -60,4 +62,13 @@ export declare const HOVER_NEEDS_POINTER_MSG = "cannot hover without a real poin
60
62
  * or throws.
61
63
  */
62
64
  export declare function tryRealInput(deps: ToolDeps, session: Session, ref: string, action: ActionType, args: Record<string, unknown>): Promise<RealActResult>;
65
+ /**
66
+ * The session a flow replay runs on, with a press sent to the lease's real keyboard.
67
+ *
68
+ * Replay dispatches its steps itself, not through `reticle_act`, so a recorded Tab replayed as a
69
+ * synthetic keydown that moves no focus, and a keyboard flow that passed live drifted on replay.
70
+ * Every other command, and a press this lease cannot drive for real, goes to the page as before.
71
+ * A Proxy, not a copy: the session keeps private state its own methods read.
72
+ */
73
+ export declare function withLeaseKeyboard(deps: ToolDeps, session: Session): Session;
63
74
  export {};
@@ -19,9 +19,10 @@
19
19
  * the base64 4/3 inflation factor applied, so the encoded payload always fits in one WebSocket
20
20
  * frame.
21
21
  */
22
- import { ActionType, DriveErrorCode, InputModeReason, ReticleCommand, TRANSPORT_LIMITS, } from '@reticlehq/core';
22
+ import { ActionType, DriveErrorCode, ElementState, INSPECT_IN_VIEWPORT_FIELD, INSPECT_REVEAL_ARG, InputMode, InputModeReason, ReticleCommand, TRANSPORT_LIMITS, } from '@reticlehq/core';
23
23
  import { boxCenter, DriveError, isRealInputAction, unspellablePressReason, } from '../../portal/input/real-input.js';
24
24
  import { assertDragNotDestructive, assertNotDestructive } from './act/act-danger.js';
25
+ import { assertNativeAllowed } from '../../portal/production/production-drive.js';
25
26
  import { NATIVE_INPUT_ARG } from '@reticlehq/core';
26
27
  import { asNumber, asRecord, asString } from '@reticlehq/core';
27
28
  import { commandOrThrow } from './tool-kit.js';
@@ -162,7 +163,10 @@ async function resolveUploadPath(rawPath, projectRoot, fs, uploadRoots, home) {
162
163
  const real = await fs.realpath(abs).catch(() => {
163
164
  throw new Error(`upload path '${rawPath}' could not be read: file not found or not accessible.`);
164
165
  });
165
- const allowed = [projectRoot, ...uploadRoots];
166
+ // The roots go through realpath too, or one path compares under two spellings: a Windows 8.3
167
+ // short name (`RUNNER~1`) against the long name realpath returns, or macOS `/tmp` against
168
+ // `/private/tmp`. A root that cannot be resolved keeps the spelling it was given.
169
+ const allowed = await Promise.all([projectRoot, ...uploadRoots].map((root) => fs.realpath(root).catch(() => root)));
166
170
  if (!allowed.some((root) => isInsideRoot(real, root))) {
167
171
  const listed = allowed.map((r) => `'${r}'`).join(', ');
168
172
  throw new Error(`upload path '${rawPath}' resolves to '${real}', which is outside every allowed upload root ` +
@@ -228,6 +232,94 @@ function synthetic(reason) {
228
232
  ? { result: undefined, settled: false }
229
233
  : { result: undefined, settled: false, reason };
230
234
  }
235
+ /** The act's args, as the real-input driver reads them. */
236
+ function performArgsOf(inner) {
237
+ const performArgs = {};
238
+ const value = asString(inner['value']);
239
+ if (value !== undefined)
240
+ performArgs.value = value;
241
+ const text = asString(inner['text']);
242
+ if (text !== undefined)
243
+ performArgs.text = text;
244
+ const key = asString(inner['key']);
245
+ if (key !== undefined)
246
+ performArgs.key = key;
247
+ const modifiers = inner['modifiers'];
248
+ if (Array.isArray(modifiers)) {
249
+ performArgs.modifiers = modifiers.filter((m) => 'string' === typeof m);
250
+ }
251
+ // A held key is the whole point of a hold-to-confirm control, and the synthetic path has honoured
252
+ // `holdMs` all along. Not forwarding it made the real keyboard the WEAKER of the two paths: it
253
+ // drove the key instantly and reported a hold that never held.
254
+ const holdMs = asNumber(inner['holdMs']);
255
+ if (holdMs !== undefined)
256
+ performArgs.holdMs = holdMs;
257
+ // `keys` and `code` are read but not forwarded: both are routed away by the caller — a multi-key
258
+ // sequence and an explicit physical code have no driver spelling — so carrying them into the
259
+ // provider would only invite it to guess at a gesture the caller did not ask for.
260
+ return performArgs;
261
+ }
262
+ /** The fields an act result names its element by, which the flow recorder anchors a step on. */
263
+ const ANCHOR_FIELDS = [
264
+ 'testid',
265
+ 'testidOf',
266
+ 'component',
267
+ 'source',
268
+ 'role',
269
+ 'name',
270
+ 'nth',
271
+ 'of',
272
+ 'secret',
273
+ ];
274
+ /**
275
+ * Press with a leased page's real keyboard, or undefined when this session is not such a lease.
276
+ *
277
+ * A lease is Reticle's own Playwright page, so it has a real keyboard with no `reticle drive`
278
+ * provider configured. Without this, Tab moved no focus and Enter or Space on a focused button did
279
+ * nothing, so a keyboard-only journey could be audited and never driven.
280
+ *
281
+ * A press that names an element focuses it first, inside the caller's `beginAction` window, the
282
+ * way a person tabs to a control and then presses the key. An element that does not take focus
283
+ * keeps the in-page press, with the reason: a person's keyboard could not reach it either.
284
+ * ponytail: lease only; the drive provider keeps its documented element-press rule.
285
+ */
286
+ async function pressOnLease(deps, session, ref, inner) {
287
+ const pool = deps.pool;
288
+ if (pool === undefined || !pool.hasKeyboard(session.id))
289
+ return undefined;
290
+ const unspellable = unspellablePressReason('', inner);
291
+ if (unspellable !== undefined)
292
+ return synthetic(unspellable);
293
+ // What the focused element is called, from the focus act's own report, so the saved step can
294
+ // find it again: the real keyboard reports no element of its own.
295
+ let anchor = {};
296
+ if (0 < ref.length) {
297
+ const focus = asRecord(await commandOrThrow(deps, session.id, ReticleCommand.ACT, {
298
+ ref,
299
+ action: ActionType.FOCUS,
300
+ args: {},
301
+ }));
302
+ anchor = Object.fromEntries(ANCHOR_FIELDS.flatMap((k) => (k in focus ? [[k, focus[k]]] : [])));
303
+ const inspected = asRecord(await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, { ref }));
304
+ const states = Array.isArray(inspected['states']) ? inspected['states'] : [];
305
+ if (!states.includes(ElementState.FOCUSED)) {
306
+ return synthetic(InputModeReason.TARGET_NOT_FOCUSABLE);
307
+ }
308
+ }
309
+ const performed = await pool.pressLease(session.id, performArgsOf(inner));
310
+ if (performed?.performed !== true)
311
+ return undefined;
312
+ return {
313
+ result: {
314
+ ...anchor,
315
+ performed: true,
316
+ action: ActionType.PRESS,
317
+ inputMode: InputMode.REAL,
318
+ ...(performed.heldMs !== undefined ? { heldMs: performed.heldMs } : {}),
319
+ },
320
+ settled: true,
321
+ };
322
+ }
231
323
  /**
232
324
  * Filler for the provider signature, which demands a box on every action.
233
325
  *
@@ -241,8 +333,36 @@ const PRESS_HAS_NO_BOX = { x: 0, y: 0, width: 0, height: 0 };
241
333
  * rather than pretend the action ran.
242
334
  */
243
335
  export const HOVER_NEEDS_POINTER_MSG = 'cannot hover without a real pointer — CSS :hover only applies to a native mouse move, never to a synthetic mouseover';
244
- function hoverSucceeded(center) {
245
- return { result: { performed: true, center, action: ActionType.HOVER }, settled: true };
336
+ /** A hover target the page could not bring into the viewport. The pointer would miss it. */
337
+ export const HOVER_OFF_VIEWPORT_MSG = 'cannot hover: the target is outside the viewport and could not be scrolled into view, so a real pointer would miss it';
338
+ /**
339
+ * INSPECT a ref for a real gesture, asking the page to scroll it into view first (#1711).
340
+ *
341
+ * A real gesture aims at coordinates. An element above or below the viewport has a box off the page,
342
+ * so the pointer lands on nothing while the provider reports `performed: true`. The synthetic path
343
+ * scrolls the target in first; this gives the real path the same step in the same round trip.
344
+ */
345
+ async function inspectRevealed(deps, session, ref) {
346
+ const inspected = await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, {
347
+ ref,
348
+ [INSPECT_REVEAL_ARG]: true,
349
+ });
350
+ const record = asRecord(inspected);
351
+ // Read the page's own measurement, taken after the scroll. `states` is no substitute: INSPECT
352
+ // builds it with `describe()`, which never adds `inViewport`, so every element read as out.
353
+ const inView = false !== record[INSPECT_IN_VIEWPORT_FIELD];
354
+ return { inspected, inView, scrolled: true === record['scrolledIntoView'] };
355
+ }
356
+ function hoverSucceeded(center, scrolled) {
357
+ return {
358
+ result: {
359
+ performed: true,
360
+ center,
361
+ action: ActionType.HOVER,
362
+ ...(scrolled ? { scrolledIntoView: true } : {}),
363
+ },
364
+ settled: true,
365
+ };
246
366
  }
247
367
  /**
248
368
  * Drive hover through a real pointer (CDP provider, then a leased Playwright page), or refuse.
@@ -256,19 +376,21 @@ async function hoverForReal(deps, session, ref, inner, provider) {
256
376
  if (!providerReady && deps.pool === undefined) {
257
377
  throw new Error(HOVER_NEEDS_POINTER_MSG);
258
378
  }
259
- const inspected = await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, { ref });
379
+ const { inspected, inView, scrolled } = await inspectRevealed(deps, session, ref);
260
380
  assertNotDestructive(ActionType.HOVER, inner, inspected);
261
381
  const box = asBox(inspected);
262
382
  if (box === undefined) {
263
383
  throw new Error(HOVER_NEEDS_POINTER_MSG);
264
384
  }
385
+ if (!inView)
386
+ throw new Error(HOVER_OFF_VIEWPORT_MSG);
265
387
  if (providerReady && provider !== undefined) {
266
388
  try {
267
389
  const performed = await provider.perform(session.url, ActionType.HOVER, box, {});
268
390
  // Hover always has a resolved box, so there is always a center to report: the provider's own
269
391
  // reading, or the box's if it reported none. Both are the same point, not a fabricated one.
270
392
  if (performed.performed)
271
- return hoverSucceeded(performed.center ?? boxCenter(box));
393
+ return hoverSucceeded(performed.center ?? boxCenter(box), scrolled);
272
394
  }
273
395
  catch {
274
396
  // A provider error used to fall back to synthetic dispatch. For hover that is a lie.
@@ -276,7 +398,7 @@ async function hoverForReal(deps, session, ref, inner, provider) {
276
398
  }
277
399
  const { cx, cy } = boxCenter(box);
278
400
  if (true === (await deps.pool?.hoverLease(session.id, cx, cy))) {
279
- return hoverSucceeded({ cx, cy });
401
+ return hoverSucceeded({ cx, cy }, scrolled);
280
402
  }
281
403
  throw new Error(HOVER_NEEDS_POINTER_MSG);
282
404
  }
@@ -297,7 +419,18 @@ export async function tryRealInput(deps, session, ref, action, args) {
297
419
  if (action === ActionType.HOVER) {
298
420
  return hoverForReal(deps, session, ref, inner, provider);
299
421
  }
422
+ // The production policy judges a command by the control it names, when the daemon dispatches
423
+ // it. A native key goes to whatever holds focus, so the policy cannot see what it presses. On a
424
+ // production drive a press runs in the page, where the daemon judges it.
425
+ if (action === ActionType.PRESS && session.production !== undefined) {
426
+ return synthetic(InputModeReason.PRODUCTION_PRESS_IN_PAGE);
427
+ }
300
428
  if (provider === undefined) {
429
+ if (action === ActionType.PRESS) {
430
+ const leased = await pressOnLease(deps, session, ref, inner);
431
+ if (leased !== undefined)
432
+ return leased;
433
+ }
301
434
  // Silent by default: with no provider EVERY action is synthetic, and a reason on all of them is
302
435
  // noise on the most-used tool in the product. But an agent that passed native:true asked a
303
436
  // question and got the opposite answer — reported from the field as a silent downgrade that cost
@@ -335,29 +468,7 @@ export async function tryRealInput(deps, session, ref, action, args) {
335
468
  }
336
469
  if (!(await provider.isAvailableFor(session.url)))
337
470
  return synthetic(InputModeReason.PAGE_NOT_CORRELATED);
338
- const performArgs = {};
339
- const value = asString(inner['value']);
340
- if (value !== undefined)
341
- performArgs.value = value;
342
- const text = asString(inner['text']);
343
- if (text !== undefined)
344
- performArgs.text = text;
345
- const key = asString(inner['key']);
346
- if (key !== undefined)
347
- performArgs.key = key;
348
- const modifiers = inner['modifiers'];
349
- if (Array.isArray(modifiers)) {
350
- performArgs.modifiers = modifiers.filter((m) => 'string' === typeof m);
351
- }
352
- // A held key is the whole point of a hold-to-confirm control, and the synthetic path has honoured
353
- // `holdMs` all along. Not forwarding it made the real keyboard the WEAKER of the two paths: it
354
- // drove the key instantly and reported a hold that never held.
355
- const holdMs = asNumber(inner['holdMs']);
356
- if (holdMs !== undefined)
357
- performArgs.holdMs = holdMs;
358
- // `keys` and `code` are read but not forwarded: both are routed away above — a multi-key sequence
359
- // and an explicit physical code have no driver spelling — so carrying them into the provider would
360
- // only invite it to guess at a gesture the caller did not ask for.
471
+ const performArgs = performArgsOf(inner);
361
472
  // A document key has no element of its own: skip INSPECT and the box entirely.
362
473
  if (action === ActionType.PRESS) {
363
474
  try {
@@ -402,25 +513,41 @@ export async function tryRealInput(deps, session, ref, action, args) {
402
513
  };
403
514
  }
404
515
  }
405
- const inspected = await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, { ref });
516
+ const source = await inspectRevealed(deps, session, ref);
517
+ const { inspected } = source;
518
+ let { scrolled } = source;
406
519
  assertNotDestructive(action, inner, inspected);
407
- const box = asBox(inspected);
408
- if (box === undefined)
520
+ await assertNativeAllowed(session.production, action, inspected);
521
+ let box = asBox(inspected);
522
+ // A box outside the viewport after the reveal is a gesture that would land on nothing. The
523
+ // synthetic path scrolls and hit-tests on its own, so it is the honest answer.
524
+ if (box === undefined || !source.inView)
409
525
  return synthetic(InputModeReason.ELEMENT_NOT_LOCATABLE);
410
526
  let toBox;
411
527
  if (action === ActionType.DRAG) {
412
528
  const toRef = asString(inner['toRef']);
413
529
  if (toRef === undefined)
414
530
  return synthetic(InputModeReason.DRAG_TARGET_UNRESOLVED);
415
- const targetInspected = await commandOrThrow(deps, session.id, ReticleCommand.INSPECT, {
416
- ref: toRef,
417
- });
531
+ const target = await inspectRevealed(deps, session, toRef);
532
+ const targetInspected = target.inspected;
418
533
  // A drag is judged on BOTH ends: dropping onto "Trash" is destructive however innocent the
419
534
  // thing being dragged looks.
420
535
  assertDragNotDestructive(inner, inspected, targetInspected);
421
536
  toBox = asBox(targetInspected);
422
- if (toBox === undefined)
537
+ if (toBox === undefined || !target.inView) {
423
538
  return synthetic(InputModeReason.DRAG_TARGET_UNRESOLVED);
539
+ }
540
+ // Scrolling the drop target in can carry the source out, so read the source again. Only when
541
+ // the page moved. If the page has to scroll AGAIN to show the source, the two ends do not fit on
542
+ // one screen, and a real drag between them cannot land.
543
+ if (target.scrolled) {
544
+ scrolled = true;
545
+ const again = await inspectRevealed(deps, session, ref);
546
+ box = asBox(again.inspected);
547
+ if (box === undefined || !again.inView || again.scrolled) {
548
+ return synthetic(InputModeReason.ELEMENT_NOT_LOCATABLE);
549
+ }
550
+ }
424
551
  }
425
552
  if (toBox !== undefined)
426
553
  performArgs.toBox = toBox;
@@ -428,7 +555,15 @@ export async function tryRealInput(deps, session, ref, action, args) {
428
555
  const performed = await provider.perform(session.url, action, box, performArgs);
429
556
  if (!performed.performed)
430
557
  return synthetic(InputModeReason.PROVIDER_DECLINED);
431
- return { result: { performed: true, center: performed.center, action }, settled: true };
558
+ return {
559
+ result: {
560
+ performed: true,
561
+ center: performed.center,
562
+ action,
563
+ ...(scrolled ? { scrolledIntoView: true } : {}),
564
+ },
565
+ settled: true,
566
+ };
432
567
  }
433
568
  catch {
434
569
  if (session.versionSkew !== undefined)
@@ -441,3 +576,36 @@ export async function tryRealInput(deps, session, ref, action, args) {
441
576
  };
442
577
  }
443
578
  }
579
+ /**
580
+ * The session a flow replay runs on, with a press sent to the lease's real keyboard.
581
+ *
582
+ * Replay dispatches its steps itself, not through `reticle_act`, so a recorded Tab replayed as a
583
+ * synthetic keydown that moves no focus, and a keyboard flow that passed live drifted on replay.
584
+ * Every other command, and a press this lease cannot drive for real, goes to the page as before.
585
+ * A Proxy, not a copy: the session keeps private state its own methods read.
586
+ */
587
+ export function withLeaseKeyboard(deps, session) {
588
+ if (deps.realInput !== undefined || deps.pool?.hasKeyboard(session.id) !== true)
589
+ return session;
590
+ const command = async (name, args = {}, timeoutMs) => {
591
+ if (ReticleCommand.ACT === name && ActionType.PRESS === args['action']) {
592
+ const ref = asString(args['ref']) ?? '';
593
+ const real = await tryRealInput(deps, session, ref, ActionType.PRESS, args);
594
+ if (real.result !== undefined) {
595
+ const result = { dispatched: true, settled: real.settled, ...asRecord(real.result) };
596
+ return { kind: 'command_result', id: ActionType.PRESS, ok: true, result };
597
+ }
598
+ }
599
+ return session.command(name, args, timeoutMs);
600
+ };
601
+ return new Proxy(session, {
602
+ get(target, key) {
603
+ if ('command' === key)
604
+ return command;
605
+ const value = Reflect.get(target, key, target);
606
+ return 'function' === typeof value
607
+ ? value.bind(target)
608
+ : value;
609
+ },
610
+ });
611
+ }
@@ -1,4 +1,5 @@
1
1
  import { bodyCaptureRemedy } from '@reticlehq/engine/evidence/body-capture-remedy.js';
2
+ import { sharedSkipReason } from '@reticlehq/engine/question/predicate/predicate-eval.js';
2
3
  import { z } from 'zod';
3
4
  import { witnessDisagreement, WitnessOutcome, } from '@reticlehq/engine/disagreement/witness-disagreement.js';
4
5
  /** How long to wait on the outside observer. Short: it is a read model, not the app under test. */
@@ -32,6 +33,7 @@ async function capturedBodies(session, since, urlContains) {
32
33
  let withBody = 0;
33
34
  let total = 0;
34
35
  let truncated = 0;
36
+ const skipReasons = [];
35
37
  for (const event of events) {
36
38
  if (event.type !== EventType.NET_REQUEST)
37
39
  continue;
@@ -40,8 +42,10 @@ async function capturedBodies(session, since, urlContains) {
40
42
  continue;
41
43
  total += 1;
42
44
  const raw = event.data['responseBody'];
43
- if (typeof raw !== 'string' || 0 === raw.length)
45
+ if (typeof raw !== 'string' || 0 === raw.length) {
46
+ skipReasons.push(event.data['responseBodyReason']);
44
47
  continue;
48
+ }
45
49
  // A capped body is not an absent one. Recover the records that closed before the cut rather than
46
50
  // reporting nothing about the page with the most data on it.
47
51
  const salvaged = salvageJson(raw);
@@ -52,7 +56,7 @@ async function capturedBodies(session, since, urlContains) {
52
56
  if (salvaged.partial)
53
57
  truncated += 1;
54
58
  }
55
- return { bodies, withBody, total, truncated };
59
+ return { bodies, withBody, total, truncated, skipReasons };
56
60
  }
57
61
  export const RECONCILE_TOOLS = [
58
62
  {
@@ -68,6 +72,10 @@ export const RECONCILE_TOOLS = [
68
72
  .string()
69
73
  .optional()
70
74
  .describe('Only compare responses from URLs containing this, e.g. "/api/v1/settlements".'),
75
+ scope: z
76
+ .string()
77
+ .optional()
78
+ .describe('A ref or CSS selector, e.g. "#status". Compare only against that subtree. Use it when the whole page is too large to read completely (a card of large native selects) and the API feeds one region. A scope that matches no element is refused, not read as an empty page.'),
71
79
  witness: z
72
80
  .object({
73
81
  url: z.string().url(),
@@ -93,7 +101,7 @@ export const RECONCILE_TOOLS = [
93
101
  handler: async (deps, args) => {
94
102
  const session = deps.sessions.resolve(asString(args['sessionId']));
95
103
  const since = asNumber(args['since']) ?? 0;
96
- const { bodies, withBody, total, truncated } = await capturedBodies(session, since, asString(args['urlContains']));
104
+ const { bodies, withBody, total, truncated, skipReasons } = await capturedBodies(session, since, asString(args['urlContains']));
97
105
  // An empty result over data that was never read is the exact false green this tool exists to
98
106
  // prevent, so the two cases are reported differently and neither is silent.
99
107
  if (0 === bodies.length) {
@@ -102,16 +110,19 @@ export const RECONCILE_TOOLS = [
102
110
  compared: 0,
103
111
  note: 0 === total
104
112
  ? 'no responses in this window matched — nothing was compared'
105
- : `${String(total)} response(s) matched but NONE carried a recorded body, so nothing was compared. ${bodyCaptureRemedy(session.sdkVersion)}`,
113
+ : `${String(total)} response(s) matched but NONE carried a recorded body, so nothing was compared. ${bodyCaptureRemedy(session.sdkVersion, sharedSkipReason(skipReasons))}`,
106
114
  });
107
115
  }
108
116
  // The whole page, INCLUDING whatever the snapshot's node cap cut off. A comparison against a
109
117
  // prefix of the page can miss the row that disagrees and can invent one that does not — the
110
118
  // value it was looking for may simply sit past the cut.
119
+ // A caller-given `scope` narrows the read to one subtree (reticle#1545); every cut branch under
120
+ // it is still finished, so a clean result is a claim about that whole subtree.
121
+ const scopeArg = asString(args['scope']);
111
122
  const page = await readCompleteTree((scope) => commandOrThrow(deps, session.id, ReticleCommand.SNAPSHOT, {
112
123
  mode: SnapshotMode.FULL,
113
124
  ...(scope === undefined ? {} : { scope, includeRoot: true }),
114
- }));
125
+ }), scopeArg);
115
126
  const mismatches = reconcile(bodies, page.tree);
116
127
  /*
117
128
  * Consult the observer outside the app, when one was named.
@@ -185,8 +196,9 @@ export const RECONCILE_TOOLS = [
185
196
  if (0 === mismatches.length && !page.complete && witness?.kind !== WitnessOutcome.DISAGREES) {
186
197
  throw new Error(`the page could not be read completely — ${page.incompleteBecause ?? 'the snapshot was cut'}. ` +
187
198
  'Nothing was found to disagree with the API, but that is not the same as nothing disagreeing: ' +
188
- 'the record that does could be in the part that was never read. Narrow the page (close overlays, ' +
189
- 'navigate to the view holding these records) and re-run.');
199
+ 'the record that does could be in the part that was never read. Narrow the read: pass `scope` ' +
200
+ '(a ref or CSS selector for the region the API feeds), close overlays, or navigate to the view ' +
201
+ 'holding these records, then re-run.');
190
202
  }
191
203
  // A truncated body is declared whether or not anything was found in it: "no mismatches" over a
192
204
  // partially-read payload is a weaker statement than over a whole one, and the difference is
@@ -197,7 +209,8 @@ export const RECONCILE_TOOLS = [
197
209
  }
198
210
  : 0 === mismatches.length
199
211
  ? {
200
- note: `compared ${String(withBody)} response(s); nothing on screen contradicts them`,
212
+ note: `compared ${String(withBody)} response(s); nothing on screen contradicts them` +
213
+ (scopeArg === undefined ? '' : ` inside \`${scopeArg}\``),
201
214
  }
202
215
  : {};
203
216
  return withControl(session, {
@@ -26,6 +26,7 @@ export interface ResultFacts {
26
26
  /** From `platformLine`. */
27
27
  platform: string;
28
28
  }
29
+ export declare const seconds: (ms: number) => string;
29
30
  /**
30
31
  * Each fact once. The headline carries the check count, so the metrics do not repeat it; with one
31
32
  * check the headline is the whole tally, so the per-word count line is left out.
@@ -40,6 +41,8 @@ export declare function retitleResult(block: string, verified: Verified, because
40
41
  export declare const KEY_REFUSED_HEAD = "Not on the platform: ";
41
42
  /** The line for a key refused for no reason the platform named. */
42
43
  export declare const KEY_REFUSED_LINE: string;
44
+ /** Linked to the platform: a dashboard, a link file or a key, refused or not. */
45
+ export declare function linkedToPlatform(root: string, env: Record<string, string | undefined>): boolean;
43
46
  /** The platform line: where the run lives when linked, what linking adds when not. One line. */
44
47
  export declare function platformLine(root: string, env: Record<string, string | undefined>, runId?: string): string;
45
48
  /**
@@ -47,7 +47,7 @@ const Said = {
47
47
  PITCH: 'Kept on this machine only. The Reticle platform adds run history, your team and Reticle Harness',
48
48
  };
49
49
  const plural = (n, word) => `${String(n)} ${word}${1 === n ? '' : 's'}`;
50
- const seconds = (ms) => ms < 60_000
50
+ export const seconds = (ms) => ms < 60_000
51
51
  ? `${(ms / 1000).toFixed(1)}s`
52
52
  : `${String(Math.floor(ms / 60_000))}m ${String(Math.round((ms % 60_000) / 1000))}s`;
53
53
  function headline(f) {
@@ -126,6 +126,13 @@ export const KEY_REFUSED_HEAD = 'Not on the platform: ';
126
126
  const keyRefusedLine = (why) => `${KEY_REFUSED_HEAD}${why.replace(/\.$/, '')}. Mint a new key in Settings → API keys; the run is kept on this machine`;
127
127
  /** The line for a key refused for no reason the platform named. */
128
128
  export const KEY_REFUSED_LINE = keyRefusedLine(KEY_REFUSED);
129
+ /** Linked to the platform: a dashboard, a link file or a key, refused or not. */
130
+ export function linkedToPlatform(root, env) {
131
+ return (keyRefusalAtLastSync(root) !== undefined ||
132
+ readDashboardUrl(root) !== undefined ||
133
+ existsSync(join(root, ReticleDir.CLOUD_LINK_FILE)) ||
134
+ apiKeyFrom(env) !== undefined);
135
+ }
129
136
  /** The platform line: where the run lives when linked, what linking adds when not. One line. */
130
137
  export function platformLine(root, env, runId) {
131
138
  // Before any link: a run the platform will refuse is not "saved" and not "on the dashboard".
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The end-of-run report: the run ledger as the agent shows it to the user.
3
+ *
4
+ * Added to the answer of the call that ends an agent's run: a session end, a yield, or a released
5
+ * tab. A run that ended by releasing its tab got no word about the run before this, and its agent
6
+ * wrote its own summary. The report is a schema'd object, the markdown built from it, and one
7
+ * instruction. The cloud line is said only when the project is not linked, and it says what it is.
8
+ */
9
+ import { RUN_REPORT_FIELD, type RunReport } from '@reticlehq/core';
10
+ import type { RunLedger } from '../../judgement/runs/run-ledger.js';
11
+ /** The report as markdown: the verdict, each claim with its evidence, then what the run left. */
12
+ export declare function renderRunReport(report: RunReport): string;
13
+ /** The calls that end an agent's run, by their own name or as an action of a merged tool. */
14
+ export declare function endsRun(tool: string, args: Record<string, unknown>): boolean;
15
+ /** What an answer says the run saved or read. An answer that refused counts for nothing. */
16
+ export declare function noteToolResult(ledger: RunLedger, tool: string, args: Record<string, unknown>, result: unknown): void;
17
+ /** The report as an answer field, or no field when the ledger holds no run. Empties the ledger. */
18
+ export declare function runReportField(ledger: RunLedger, root: string | undefined, env: Record<string, string | undefined>, now: number): Partial<Record<typeof RUN_REPORT_FIELD, {
19
+ report: RunReport;
20
+ markdown: string;
21
+ instruction: string;
22
+ }>>;