@reticlehq/server 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (614) hide show
  1. package/SKILL.md +122 -123
  2. package/dist/command/cli/adhoc-suite.d.ts +14 -0
  3. package/dist/command/cli/adhoc-suite.js +42 -7
  4. package/dist/command/cli/adhoc-verdict.d.ts +18 -2
  5. package/dist/command/cli/adhoc-verdict.js +70 -6
  6. package/dist/command/cli/answers/gate-exit.d.ts +23 -0
  7. package/dist/command/cli/answers/gate-exit.js +45 -0
  8. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  9. package/dist/command/cli/answers/gate-hook-message.js +3 -1
  10. package/dist/command/cli/auth/device-flow.d.ts +13 -2
  11. package/dist/command/cli/auth/device-flow.js +17 -0
  12. package/dist/command/cli/cli-doctor.js +14 -0
  13. package/dist/command/cli/cli-flow-commands.d.ts +0 -11
  14. package/dist/command/cli/cli-flow-commands.js +28 -105
  15. package/dist/command/cli/cli-kill.js +3 -2
  16. package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
  17. package/dist/command/cli/cli-parse-grammar.js +75 -0
  18. package/dist/command/cli/cli-parse-verify.d.ts +2 -0
  19. package/dist/command/cli/cli-parse-verify.js +18 -0
  20. package/dist/command/cli/cli-parse.d.ts +19 -28
  21. package/dist/command/cli/cli-parse.js +82 -182
  22. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  23. package/dist/command/cli/cli-update-commands.js +61 -9
  24. package/dist/command/cli/cli-usage.d.ts +33 -0
  25. package/dist/command/cli/cli-usage.js +287 -0
  26. package/dist/command/cli/cli-verify.d.ts +34 -4
  27. package/dist/command/cli/cli-verify.js +81 -12
  28. package/dist/command/cli/cloud-cli.d.ts +13 -0
  29. package/dist/command/cli/cloud-cli.js +145 -333
  30. package/dist/command/cli/cloud-kit.d.ts +12 -0
  31. package/dist/command/cli/cloud-kit.js +41 -2
  32. package/dist/command/cli/cloud-link.d.ts +57 -0
  33. package/dist/command/cli/cloud-link.js +308 -0
  34. package/dist/command/cli/cloud-login.d.ts +15 -1
  35. package/dist/command/cli/cloud-login.js +67 -13
  36. package/dist/command/cli/context-command.d.ts +1 -0
  37. package/dist/command/cli/context-command.js +33 -0
  38. package/dist/command/cli/daemon-start-options.d.ts +3 -0
  39. package/dist/command/cli/daemon-start-options.js +24 -3
  40. package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
  41. package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
  42. package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
  43. package/dist/command/cli/doctor/doctor-project-line.js +5 -4
  44. package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
  45. package/dist/command/cli/doctor/doctor-rows.js +6 -0
  46. package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
  47. package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
  48. package/dist/command/cli/known-regression.d.ts +13 -0
  49. package/dist/command/cli/known-regression.js +34 -0
  50. package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
  51. package/dist/command/cli/launch/sdk-version-match.js +25 -0
  52. package/dist/command/cli/lifecycle/daemon-lifecycle.js +11 -2
  53. package/dist/command/cli/mcp-command.d.ts +6 -0
  54. package/dist/command/cli/mcp-command.js +13 -5
  55. package/dist/command/cli/ports/port-holder.js +1 -1
  56. package/dist/command/cli/project-name.d.ts +29 -1
  57. package/dist/command/cli/project-name.js +107 -2
  58. package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
  59. package/dist/command/cli/setup-mcp-cli.js +29 -8
  60. package/dist/command/cli/status/status-lines.d.ts +1 -0
  61. package/dist/command/cli/status/status-lines.js +47 -2
  62. package/dist/command/cli/status/workspace-links.d.ts +10 -0
  63. package/dist/command/cli/status/workspace-links.js +29 -0
  64. package/dist/command/cli/try-command.d.ts +96 -0
  65. package/dist/command/cli/try-command.js +362 -0
  66. package/dist/command/cli/tutorial.d.ts +0 -100
  67. package/dist/command/cli/tutorial.js +0 -178
  68. package/dist/command/cli/verify-results.d.ts +19 -0
  69. package/dist/command/cli/verify-results.js +29 -0
  70. package/dist/command/cli-onboarding.d.ts +0 -11
  71. package/dist/command/cli-onboarding.js +0 -15
  72. package/dist/command/cli.d.ts +14 -0
  73. package/dist/command/cli.js +103 -83
  74. package/dist/command/daemon/daemon-resilience.js +4 -4
  75. package/dist/command/daemon/daemon-resolve.d.ts +9 -1
  76. package/dist/command/daemon/daemon-resolve.js +49 -10
  77. package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
  78. package/dist/command/setup/bringup/bridge-port.js +13 -3
  79. package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
  80. package/dist/command/setup/bringup/ensure-daemon.js +9 -2
  81. package/dist/command/setup/init/agent-restart.d.ts +23 -0
  82. package/dist/command/setup/init/agent-restart.js +28 -0
  83. package/dist/command/setup/init/init-port.d.ts +10 -1
  84. package/dist/command/setup/init/init-port.js +22 -2
  85. package/dist/command/setup/init/init-runtime.d.ts +12 -2
  86. package/dist/command/setup/init/init-runtime.js +96 -37
  87. package/dist/command/setup/node-effects.d.ts +2 -0
  88. package/dist/command/setup/node-effects.js +36 -8
  89. package/dist/command/setup/probe/existing-app.d.ts +12 -0
  90. package/dist/command/setup/probe/existing-app.js +14 -0
  91. package/dist/command/setup/remaining-steps.js +1 -1
  92. package/dist/command/setup/run-setup.d.ts +29 -9
  93. package/dist/command/setup/run-setup.js +34 -28
  94. package/dist/command/setup/setup-command.d.ts +23 -0
  95. package/dist/command/setup/setup-command.js +57 -4
  96. package/dist/command/setup/setup-mcp.js +25 -2
  97. package/dist/command/setup/tauri-signing.d.ts +19 -0
  98. package/dist/command/setup/tauri-signing.js +79 -0
  99. package/dist/command/setup/terminal/listeners.d.ts +10 -1
  100. package/dist/command/setup/terminal/listeners.js +37 -6
  101. package/dist/command/update/update-nudge.d.ts +9 -0
  102. package/dist/command/update/update-nudge.js +1 -1
  103. package/dist/command/update/updater.js +11 -15
  104. package/dist/command/version/version-nudge.js +3 -0
  105. package/dist/command/version/version-skew.d.ts +20 -1
  106. package/dist/command/version/version-skew.js +38 -2
  107. package/dist/features/crawl/crawl.js +14 -1
  108. package/dist/features/exhaust/ledger.d.ts +14 -14
  109. package/dist/features/harness/drive-report.d.ts +39 -5
  110. package/dist/features/harness/drive-report.js +271 -54
  111. package/dist/features/harness/drive-runs.d.ts +120 -0
  112. package/dist/features/harness/drive-runs.js +289 -0
  113. package/dist/features/harness/drivers.d.ts +1 -1
  114. package/dist/features/harness/drivers.js +1 -1
  115. package/dist/features/harness/explore/console-pages.d.ts +15 -0
  116. package/dist/features/harness/explore/console-pages.js +146 -0
  117. package/dist/features/harness/explore/disparity.d.ts +19 -0
  118. package/dist/features/harness/explore/disparity.js +71 -0
  119. package/dist/features/harness/explore/fake-app.d.ts +16 -0
  120. package/dist/features/harness/explore/fake-app.js +49 -0
  121. package/dist/features/harness/explore/goal-search.d.ts +26 -0
  122. package/dist/features/harness/explore/goal-search.js +144 -0
  123. package/dist/features/harness/explore/page-tree.d.ts +49 -0
  124. package/dist/features/harness/explore/page-tree.js +148 -0
  125. package/dist/features/harness/explore/site-map.d.ts +34 -0
  126. package/dist/features/harness/explore/site-map.js +150 -0
  127. package/dist/features/harness/goals.d.ts +68 -7
  128. package/dist/features/harness/goals.js +289 -36
  129. package/dist/features/harness/harness.d.ts +30 -1
  130. package/dist/features/harness/harness.js +141 -7
  131. package/dist/features/harness/platform/local-apps.d.ts +36 -3
  132. package/dist/features/harness/platform/local-apps.js +81 -5
  133. package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
  134. package/dist/features/harness/platform/persona-credentials.js +69 -0
  135. package/dist/features/harness/platform/platform-drives.d.ts +61 -0
  136. package/dist/features/harness/platform/platform-drives.js +114 -0
  137. package/dist/features/harness/platform/refusal.d.ts +52 -0
  138. package/dist/features/harness/platform/refusal.js +71 -0
  139. package/dist/features/harness/platform/remote-drive.d.ts +8 -2
  140. package/dist/features/harness/platform/remote-drive.js +22 -9
  141. package/dist/features/harness/platform/script.d.ts +57 -11
  142. package/dist/features/harness/platform/script.js +144 -12
  143. package/dist/features/harness/platform/server-driver.d.ts +17 -4
  144. package/dist/features/harness/platform/server-driver.js +29 -26
  145. package/dist/features/harness/platform-config.d.ts +38 -5
  146. package/dist/features/harness/platform-config.js +93 -8
  147. package/dist/features/harness/route-guess.d.ts +13 -0
  148. package/dist/features/harness/route-guess.js +49 -0
  149. package/dist/features/harness/script/script-run.d.ts +30 -1
  150. package/dist/features/harness/script/script-run.js +355 -48
  151. package/dist/features/license/license.d.ts +2 -2
  152. package/dist/features/visual/step-shots.d.ts +31 -0
  153. package/dist/features/visual/step-shots.js +231 -0
  154. package/dist/features/visual/visual-tools.js +22 -14
  155. package/dist/hooks/client-directory.d.ts +18 -0
  156. package/dist/hooks/client-directory.js +57 -0
  157. package/dist/hooks/coding-agents.d.ts +34 -0
  158. package/dist/hooks/coding-agents.js +117 -0
  159. package/dist/hooks/driven-by.d.ts +21 -2
  160. package/dist/hooks/driven-by.js +42 -2
  161. package/dist/hooks/hook-bus.d.ts +4 -0
  162. package/dist/hooks/hook-bus.js +18 -0
  163. package/dist/index.d.ts +1 -0
  164. package/dist/index.js +26 -183
  165. package/dist/judgement/capsule/capsule-store.d.ts +2 -2
  166. package/dist/judgement/capsule/causal-summary.d.ts +15 -1
  167. package/dist/judgement/capsule/causal-summary.js +33 -0
  168. package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
  169. package/dist/judgement/runs/artifact/build-verification-run.js +20 -4
  170. package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
  171. package/dist/judgement/runs/artifact/gap-summary.js +12 -3
  172. package/dist/judgement/runs/artifact/replay-mapping.js +25 -9
  173. package/dist/judgement/runs/artifact/run-store.d.ts +13 -1
  174. package/dist/judgement/runs/artifact/run-store.js +175 -8
  175. package/dist/judgement/runs/context-tools.js +59 -1
  176. package/dist/judgement/runs/default-run-id.d.ts +3 -0
  177. package/dist/judgement/runs/default-run-id.js +16 -0
  178. package/dist/judgement/runs/drive-run.d.ts +53 -5
  179. package/dist/judgement/runs/drive-run.js +234 -20
  180. package/dist/judgement/runs/resume.d.ts +44 -0
  181. package/dist/judgement/runs/resume.js +137 -0
  182. package/dist/judgement/runs/reticle-runner.js +5 -2
  183. package/dist/judgement/runs/runner-port.d.ts +5 -1
  184. package/dist/judgement/runs/runner-port.js +8 -2
  185. package/dist/judgement/runs/verification-sync.d.ts +2 -0
  186. package/dist/judgement/runs/verification-sync.js +36 -15
  187. package/dist/language/flows/decision.js +2 -1
  188. package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
  189. package/dist/language/flows/fields/flow-secret-field.js +25 -2
  190. package/dist/language/flows/flow-anchor.d.ts +7 -2
  191. package/dist/language/flows/flow-anchor.js +10 -3
  192. package/dist/language/flows/flow-intent.d.ts +1 -0
  193. package/dist/language/flows/flow-journey.d.ts +6 -0
  194. package/dist/language/flows/flow-journey.js +20 -0
  195. package/dist/language/flows/flow-learning.d.ts +4 -0
  196. package/dist/language/flows/flow-learning.js +37 -3
  197. package/dist/language/flows/flow-replay-run.js +16 -18
  198. package/dist/language/flows/flow-replay-types.d.ts +1 -1
  199. package/dist/language/flows/flow-replay.d.ts +18 -1
  200. package/dist/language/flows/flow-replay.js +22 -7
  201. package/dist/language/flows/flow-result.d.ts +22 -1
  202. package/dist/language/flows/flow-result.js +47 -2
  203. package/dist/language/flows/flow-step-runners.d.ts +2 -0
  204. package/dist/language/flows/flow-step-runners.js +12 -6
  205. package/dist/language/flows/flow-store-for-session.js +5 -1
  206. package/dist/language/flows/flow-tools.d.ts +10 -0
  207. package/dist/language/flows/flow-tools.js +61 -62
  208. package/dist/language/flows/flows.d.ts +16 -1
  209. package/dist/language/flows/flows.js +69 -10
  210. package/dist/language/flows/narrow-write.d.ts +7 -0
  211. package/dist/language/flows/narrow-write.js +36 -1
  212. package/dist/language/flows/recording/recording-backtrack.js +14 -1
  213. package/dist/language/flows/recording/tape/recordings.d.ts +29 -1
  214. package/dist/language/flows/recording/tape/recordings.js +60 -10
  215. package/dist/language/flows/replay.js +29 -6
  216. package/dist/language/flows/single-replay.d.ts +7 -0
  217. package/dist/language/flows/single-replay.js +26 -0
  218. package/dist/language/flows/verify-change-tools.d.ts +1 -0
  219. package/dist/language/flows/verify-change-tools.js +200 -193
  220. package/dist/memory/cloud/cloud-config.d.ts +10 -1
  221. package/dist/memory/cloud/cloud-config.js +19 -5
  222. package/dist/memory/cloud/cloud-sync.js +3 -3
  223. package/dist/memory/cloud/harness-config.d.ts +49 -0
  224. package/dist/memory/cloud/harness-config.js +16 -20
  225. package/dist/memory/cloud/memory-scope.d.ts +17 -0
  226. package/dist/memory/cloud/memory-scope.js +22 -0
  227. package/dist/memory/cloud/sync-cycle.d.ts +32 -4
  228. package/dist/memory/cloud/sync-cycle.js +113 -12
  229. package/dist/memory/cloud/sync-daemon.d.ts +11 -0
  230. package/dist/memory/cloud/sync-daemon.js +114 -4
  231. package/dist/memory/cloud/sync-disk.js +26 -2
  232. package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
  233. package/dist/memory/cloud/sync-screenshots.js +105 -0
  234. package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
  235. package/dist/memory/cloud/unsynced-roots.js +166 -0
  236. package/dist/memory/impact/account-watch.d.ts +16 -1
  237. package/dist/memory/impact/account-watch.js +26 -1
  238. package/dist/memory/impact/impact-store.d.ts +1 -4
  239. package/dist/memory/impact/impact-store.js +32 -42
  240. package/dist/memory/intent/inline-intent.d.ts +1 -0
  241. package/dist/memory/intent/intent-shard-store.js +16 -4
  242. package/dist/memory/intent/intent-shard.d.ts +12 -0
  243. package/dist/memory/intent/intent-store.d.ts +1 -0
  244. package/dist/memory/intent/intent-tools.js +42 -2
  245. package/dist/memory/intent/open-intents.d.ts +6 -0
  246. package/dist/memory/intent/open-intents.js +10 -1
  247. package/dist/memory/intent/settled-words.d.ts +24 -0
  248. package/dist/memory/intent/settled-words.js +130 -0
  249. package/dist/memory/journal/drive-flow.d.ts +16 -0
  250. package/dist/memory/journal/drive-flow.js +34 -2
  251. package/dist/memory/journal/drive-run-flush.d.ts +2 -0
  252. package/dist/memory/journal/drive-run-flush.js +20 -5
  253. package/dist/memory/journal/journal-recorder.js +46 -3
  254. package/dist/memory/journal/on-disk/retention.d.ts +5 -0
  255. package/dist/memory/journal/on-disk/retention.js +27 -2
  256. package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
  257. package/dist/memory/journal/session-end.d.ts +15 -1
  258. package/dist/memory/journal/session-end.js +38 -7
  259. package/dist/memory/project/artifact-root-resolver.js +33 -5
  260. package/dist/memory/project/artifact-root.d.ts +4 -3
  261. package/dist/memory/project/artifact-root.js +2 -2
  262. package/dist/memory/project/dir/reticle-dir.d.ts +14 -0
  263. package/dist/memory/project/dir/reticle-dir.js +23 -2
  264. package/dist/memory/project/link-directory.d.ts +15 -0
  265. package/dist/memory/project/link-directory.js +44 -0
  266. package/dist/memory/project/root-claims.d.ts +6 -0
  267. package/dist/memory/project/root-claims.js +39 -0
  268. package/dist/memory/project/session-root.d.ts +2 -0
  269. package/dist/memory/project/session-root.js +9 -1
  270. package/dist/memory/project/sync-status.d.ts +46 -0
  271. package/dist/memory/project/sync-status.js +135 -1
  272. package/dist/memory/recall/memory-tools.js +46 -3
  273. package/dist/memory/recall/platform-memory.d.ts +34 -0
  274. package/dist/memory/recall/platform-memory.js +139 -0
  275. package/dist/memory/recall/project-memory.js +36 -8
  276. package/dist/portal/bridge/bridge.d.ts +9 -5
  277. package/dist/portal/bridge/bridge.js +66 -23
  278. package/dist/portal/bridge/harness-request.d.ts +23 -0
  279. package/dist/portal/bridge/harness-request.js +27 -0
  280. package/dist/portal/bridge/wrong-path.d.ts +17 -0
  281. package/dist/portal/bridge/wrong-path.js +35 -0
  282. package/dist/portal/input/network-mock-tools.js +11 -2
  283. package/dist/portal/input/real-input.d.ts +2 -0
  284. package/dist/portal/input/real-input.js +13 -10
  285. package/dist/portal/pool/browser-pool.d.ts +48 -0
  286. package/dist/portal/pool/browser-pool.js +120 -2
  287. package/dist/portal/pool/context-permissions.d.ts +33 -0
  288. package/dist/portal/pool/context-permissions.js +33 -0
  289. package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
  290. package/dist/portal/pool/lease-marker-stamp.js +49 -0
  291. package/dist/portal/pool/playwright-launcher.d.ts +27 -5
  292. package/dist/portal/pool/playwright-launcher.js +59 -14
  293. package/dist/portal/pool/pool-contract.d.ts +21 -1
  294. package/dist/portal/session/agent-link.d.ts +8 -0
  295. package/dist/portal/session/agent-link.js +17 -0
  296. package/dist/portal/session/command-payload.js +6 -1
  297. package/dist/portal/session/daemon-answers.d.ts +3 -3
  298. package/dist/portal/session/daemon-answers.js +4 -2
  299. package/dist/portal/session/dev-server/dev-command.d.ts +9 -0
  300. package/dist/portal/session/dev-server/dev-command.js +27 -1
  301. package/dist/portal/session/facts/observed-state.d.ts +16 -18
  302. package/dist/portal/session/facts/observed-state.js +46 -3
  303. package/dist/portal/session/human/live-control.d.ts +9 -2
  304. package/dist/portal/session/human/live-control.js +12 -3
  305. package/dist/portal/session/live-control-tools.js +82 -13
  306. package/dist/portal/session/narration-args.d.ts +5 -0
  307. package/dist/portal/session/narration-args.js +17 -0
  308. package/dist/portal/session/no-session-diagnosis.d.ts +1 -1
  309. package/dist/portal/session/no-session-diagnosis.js +31 -9
  310. package/dist/portal/session/no-session-next-action.d.ts +20 -0
  311. package/dist/portal/session/no-session-next-action.js +134 -61
  312. package/dist/portal/session/no-session-watch.js +179 -22
  313. package/dist/portal/session/presence/session-lease.js +1 -1
  314. package/dist/portal/session/recorded-gaps.d.ts +97 -0
  315. package/dist/portal/session/recorded-gaps.js +251 -0
  316. package/dist/portal/session/session-health.d.ts +11 -0
  317. package/dist/portal/session/session-health.js +3 -1
  318. package/dist/portal/session/session-info.js +2 -1
  319. package/dist/portal/session/session-manager.d.ts +18 -0
  320. package/dist/portal/session/session-manager.js +54 -3
  321. package/dist/portal/session/session-successor.d.ts +8 -0
  322. package/dist/portal/session/session-successor.js +1 -1
  323. package/dist/portal/session/session.d.ts +15 -28
  324. package/dist/portal/session/session.js +60 -61
  325. package/dist/portal/session/timing/command-timeout.js +25 -1
  326. package/dist/portal/session/timing/wall-clock.d.ts +1 -1
  327. package/dist/portal/session/timing/wall-clock.js +1 -1
  328. package/dist/portal/session/zero-install-first-call.d.ts +31 -0
  329. package/dist/portal/session/zero-install-first-call.js +88 -0
  330. package/dist/start-options.d.ts +7 -1
  331. package/dist/start-wiring.d.ts +63 -0
  332. package/dist/start-wiring.js +169 -0
  333. package/dist/status-payload.d.ts +13 -1
  334. package/dist/status-payload.js +13 -1
  335. package/dist/surface/http-server.js +7 -1
  336. package/dist/surface/mcp/attach-memory.d.ts +1 -0
  337. package/dist/surface/mcp/attach-memory.js +12 -1
  338. package/dist/surface/mcp/mcp-proxy.d.ts +4 -0
  339. package/dist/surface/mcp/mcp-proxy.js +29 -8
  340. package/dist/surface/mcp/mcp.d.ts +23 -0
  341. package/dist/surface/mcp/mcp.js +34 -4
  342. package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
  343. package/dist/surface/mcp/proxy/drive-calls.js +27 -0
  344. package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
  345. package/dist/surface/mcp/server-instructions.js +22 -14
  346. package/dist/surface/mcp/shared-params.js +1 -1
  347. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  348. package/dist/surface/mcp/surface-vocabulary.js +1 -0
  349. package/dist/surface/tools/account-tool.d.ts +22 -0
  350. package/dist/surface/tools/account-tool.js +157 -0
  351. package/dist/surface/tools/act/act-danger.d.ts +11 -0
  352. package/dist/surface/tools/act/act-danger.js +38 -1
  353. package/dist/surface/tools/act/act-observation.d.ts +33 -0
  354. package/dist/surface/tools/act/act-observation.js +57 -0
  355. package/dist/surface/tools/act/act-preflight.js +25 -2
  356. package/dist/surface/tools/act/act-target.js +8 -3
  357. package/dist/surface/tools/act/resolve-target.d.ts +1 -1
  358. package/dist/surface/tools/act/resolve-target.js +18 -3
  359. package/dist/surface/tools/act/screen-request.d.ts +34 -0
  360. package/dist/surface/tools/act/screen-request.js +113 -0
  361. package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
  362. package/dist/surface/tools/act/sequence-grade.js +13 -2
  363. package/dist/surface/tools/act-sequence-tool.js +136 -27
  364. package/dist/surface/tools/act-tools.d.ts +0 -9
  365. package/dist/surface/tools/act-tools.js +48 -34
  366. package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
  367. package/dist/surface/tools/agent/app-notes-learning.js +80 -0
  368. package/dist/surface/tools/agent/app-notes.d.ts +4 -0
  369. package/dist/surface/tools/agent/app-notes.js +48 -0
  370. package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
  371. package/dist/surface/tools/agent/build-staleness.js +62 -0
  372. package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
  373. package/dist/surface/tools/agent/compact-envelope.js +374 -0
  374. package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
  375. package/dist/surface/tools/agent/for-the-agent.js +44 -0
  376. package/dist/surface/tools/agent/notices.d.ts +50 -0
  377. package/dist/surface/tools/agent/notices.js +152 -0
  378. package/dist/surface/tools/agent/reteach.d.ts +11 -0
  379. package/dist/surface/tools/agent/reteach.js +50 -0
  380. package/dist/surface/tools/args/alias-args.d.ts +5 -0
  381. package/dist/surface/tools/args/alias-args.js +10 -0
  382. package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
  383. package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
  384. package/dist/surface/tools/assert/assert-source.d.ts +15 -0
  385. package/dist/surface/tools/assert/assert-source.js +40 -0
  386. package/dist/surface/tools/assert/assert-verdict.js +25 -15
  387. package/dist/surface/tools/browser-tools.js +43 -1
  388. package/dist/surface/tools/chat-drives.d.ts +75 -0
  389. package/dist/surface/tools/chat-drives.js +265 -21
  390. package/dist/surface/tools/coverage-tools.d.ts +2 -10
  391. package/dist/surface/tools/coverage-tools.js +2 -53
  392. package/dist/surface/tools/dynamic-tools.js +29 -1
  393. package/dist/surface/tools/empty-session-list.d.ts +10 -0
  394. package/dist/surface/tools/empty-session-list.js +45 -5
  395. package/dist/surface/tools/error-recovery.d.ts +8 -0
  396. package/dist/surface/tools/error-recovery.js +69 -5
  397. package/dist/surface/tools/explore-tools.d.ts +23 -2
  398. package/dist/surface/tools/explore-tools.js +377 -74
  399. package/dist/surface/tools/feedback-tools.d.ts +2 -3
  400. package/dist/surface/tools/feedback-tools.js +4 -3
  401. package/dist/surface/tools/gap-novelty.d.ts +3 -1
  402. package/dist/surface/tools/gap-novelty.js +8 -1
  403. package/dist/surface/tools/harness-explore.d.ts +52 -73
  404. package/dist/surface/tools/harness-explore.js +177 -146
  405. package/dist/surface/tools/harness-gate.d.ts +88 -0
  406. package/dist/surface/tools/harness-gate.js +235 -0
  407. package/dist/surface/tools/harness-hunt.d.ts +43 -0
  408. package/dist/surface/tools/harness-hunt.js +215 -0
  409. package/dist/surface/tools/harness-plan.d.ts +5 -0
  410. package/dist/surface/tools/harness-plan.js +7 -0
  411. package/dist/surface/tools/harness-script.d.ts +22 -0
  412. package/dist/surface/tools/harness-script.js +256 -38
  413. package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
  414. package/dist/surface/tools/harness-tab-guard.js +27 -0
  415. package/dist/surface/tools/harness-toolset.d.ts +8 -0
  416. package/dist/surface/tools/harness-toolset.js +27 -4
  417. package/dist/surface/tools/human-notes.d.ts +16 -0
  418. package/dist/surface/tools/human-notes.js +27 -0
  419. package/dist/surface/tools/invoke-tool.d.ts +5 -2
  420. package/dist/surface/tools/invoke-tool.js +220 -93
  421. package/dist/surface/tools/lease-hint.js +26 -5
  422. package/dist/surface/tools/lease-permissions.d.ts +33 -0
  423. package/dist/surface/tools/lease-permissions.js +73 -0
  424. package/dist/surface/tools/lease-readiness.d.ts +38 -0
  425. package/dist/surface/tools/lease-readiness.js +57 -0
  426. package/dist/surface/tools/lease-seed.d.ts +20 -0
  427. package/dist/surface/tools/lease-seed.js +45 -1
  428. package/dist/surface/tools/lease-tools.d.ts +10 -1
  429. package/dist/surface/tools/lease-tools.js +119 -69
  430. package/dist/surface/tools/live-call-text.js +2 -2
  431. package/dist/surface/tools/merge-tools.js +42 -2
  432. package/dist/surface/tools/merged-name-redirect.js +4 -0
  433. package/dist/surface/tools/next-step.d.ts +11 -1
  434. package/dist/surface/tools/next-step.js +73 -18
  435. package/dist/surface/tools/observe-tools.js +43 -5
  436. package/dist/surface/tools/platform-moment.d.ts +60 -0
  437. package/dist/surface/tools/platform-moment.js +132 -4
  438. package/dist/surface/tools/read-tools.js +65 -14
  439. package/dist/surface/tools/reticle-result.d.ts +120 -0
  440. package/dist/surface/tools/reticle-result.js +364 -0
  441. package/dist/surface/tools/tool-kit.d.ts +68 -2
  442. package/dist/surface/tools/tool-kit.js +73 -1
  443. package/dist/surface/tools/tools.js +25 -13
  444. package/dist/telemetry/account-envelope.d.ts +7 -0
  445. package/dist/telemetry/account-envelope.js +61 -0
  446. package/dist/telemetry/agent-client.d.ts +15 -0
  447. package/dist/telemetry/agent-client.js +57 -0
  448. package/dist/telemetry/app-instrumented.js +9 -2
  449. package/dist/telemetry/argument-shape.d.ts +7 -0
  450. package/dist/telemetry/argument-shape.js +86 -1
  451. package/dist/telemetry/automation-hint.d.ts +2 -3
  452. package/dist/telemetry/automation-hint.js +33 -8
  453. package/dist/telemetry/cli-telemetry.js +7 -2
  454. package/dist/telemetry/daemon-telemetry.js +3 -0
  455. package/dist/telemetry/error-fingerprint.d.ts +10 -1
  456. package/dist/telemetry/error-fingerprint.js +61 -5
  457. package/dist/telemetry/feedback-cli.d.ts +0 -13
  458. package/dist/telemetry/feedback-cli.js +0 -77
  459. package/dist/telemetry/feedback.d.ts +13 -0
  460. package/dist/telemetry/feedback.js +183 -17
  461. package/dist/telemetry/harness-run.d.ts +18 -0
  462. package/dist/telemetry/harness-run.js +80 -0
  463. package/dist/telemetry/hud-events.d.ts +16 -0
  464. package/dist/telemetry/hud-events.js +27 -0
  465. package/dist/telemetry/hud-metrics.d.ts +27 -6
  466. package/dist/telemetry/hud-metrics.js +99 -25
  467. package/dist/telemetry/init-telemetry.js +2 -2
  468. package/dist/telemetry/install-source.d.ts +13 -25
  469. package/dist/telemetry/install-source.js +44 -3
  470. package/dist/telemetry/mcp-connection.d.ts +0 -14
  471. package/dist/telemetry/mcp-connection.js +5 -1
  472. package/dist/telemetry/onboarding-firsts.d.ts +32 -0
  473. package/dist/telemetry/onboarding-firsts.js +81 -6
  474. package/dist/telemetry/platform-linked.d.ts +5 -0
  475. package/dist/telemetry/platform-linked.js +32 -0
  476. package/dist/telemetry/project-profile.js +5 -3
  477. package/dist/telemetry/proxy-telemetry.js +4 -1
  478. package/dist/telemetry/session-metrics.d.ts +15 -4
  479. package/dist/telemetry/session-metrics.js +88 -52
  480. package/dist/telemetry/telemetry.d.ts +26 -2
  481. package/dist/telemetry/telemetry.js +102 -21
  482. package/dist/telemetry/verification-of.d.ts +3 -1
  483. package/dist/telemetry/verification-of.js +12 -4
  484. package/dist/wire-journal.d.ts +2 -2
  485. package/dist/wire-journal.js +16 -5
  486. package/docs/README.md +2 -2
  487. package/docs/actions.mdx +6 -3
  488. package/docs/adapters.md +1 -1
  489. package/docs/agent-cheatsheet.md +24 -27
  490. package/docs/architecture.md +5 -5
  491. package/docs/autodrive.md +13 -12
  492. package/docs/benchmarks.md +5 -5
  493. package/docs/best-practices.mdx +27 -9
  494. package/docs/capabilities.mdx +14 -16
  495. package/docs/cli/affected.mdx +1 -1
  496. package/docs/cli/cloud.mdx +54 -75
  497. package/docs/cli/doctor.mdx +8 -3
  498. package/docs/cli/drive.mdx +12 -12
  499. package/docs/cli/feedback.mdx +3 -3
  500. package/docs/cli/gate.mdx +7 -2
  501. package/docs/cli/help.mdx +50 -55
  502. package/docs/cli/init.mdx +32 -15
  503. package/docs/cli/license.mdx +12 -8
  504. package/docs/cli/mcp.mdx +4 -4
  505. package/docs/cli/open.mdx +3 -3
  506. package/docs/cli/report.mdx +4 -2
  507. package/docs/cli/restart.mdx +2 -2
  508. package/docs/cli/rollback.mdx +14 -8
  509. package/docs/cli/serve.mdx +4 -4
  510. package/docs/cli/setup.mdx +23 -0
  511. package/docs/cli/status.mdx +13 -4
  512. package/docs/cli/stop.mdx +67 -8
  513. package/docs/cli/try.mdx +62 -0
  514. package/docs/cli/update.mdx +4 -3
  515. package/docs/cli/verify.mdx +17 -6
  516. package/docs/cli/version.mdx +3 -2
  517. package/docs/cli.mdx +110 -41
  518. package/docs/debugging.md +3 -3
  519. package/docs/deploy-checks.md +1 -1
  520. package/docs/desktop.mdx +5 -3
  521. package/docs/enterprise.md +11 -10
  522. package/docs/environment.mdx +5 -2
  523. package/docs/faq.mdx +3 -3
  524. package/docs/first-drive.md +5 -4
  525. package/docs/flows.md +35 -27
  526. package/docs/for-agents.md +4 -5
  527. package/docs/frameworks.mdx +32 -21
  528. package/docs/getting-started.md +66 -64
  529. package/docs/human-control.md +15 -14
  530. package/docs/index.mdx +15 -43
  531. package/docs/install-agentic.mdx +22 -37
  532. package/docs/install-manual.mdx +22 -12
  533. package/docs/instrumentation.mdx +5 -3
  534. package/docs/integration-patterns.md +7 -7
  535. package/docs/license-activation.mdx +1 -1
  536. package/docs/multi-agent-testing.md +24 -6
  537. package/docs/packages/babel-plugin.mdx +4 -2
  538. package/docs/packages/browser.mdx +16 -10
  539. package/docs/packages/core.mdx +12 -5
  540. package/docs/packages/electron.mdx +3 -2
  541. package/docs/packages/engine.mdx +1 -1
  542. package/docs/packages/eslint-plugin.mdx +18 -12
  543. package/docs/packages/next.mdx +8 -7
  544. package/docs/packages/open-verification.mdx +1 -1
  545. package/docs/packages/server.mdx +5 -5
  546. package/docs/packages/tauri.mdx +8 -8
  547. package/docs/packages/test.mdx +4 -10
  548. package/docs/packages/vite-plugin.mdx +11 -4
  549. package/docs/packages.mdx +11 -15
  550. package/docs/platform-integration.md +6 -5
  551. package/docs/predicates.mdx +40 -24
  552. package/docs/quickstart.mdx +58 -29
  553. package/docs/required-checks.md +8 -7
  554. package/docs/reticle-manual.md +15 -5
  555. package/docs/skill-file.mdx +17 -79
  556. package/docs/skill-setup.md +9 -9
  557. package/docs/skill.md +200 -0
  558. package/docs/system-map.md +7 -7
  559. package/docs/telemetry.md +22 -24
  560. package/docs/testing.md +4 -4
  561. package/docs/token-efficiency.md +12 -12
  562. package/docs/tools/act-and-wait.mdx +9 -2
  563. package/docs/tools/act-sequence.mdx +6 -7
  564. package/docs/tools/act.mdx +2 -2
  565. package/docs/tools/assert.mdx +18 -17
  566. package/docs/tools/console.mdx +9 -9
  567. package/docs/tools/inspect.mdx +16 -7
  568. package/docs/tools/look.mdx +24 -0
  569. package/docs/tools/navigate.mdx +5 -3
  570. package/docs/tools/network.mdx +2 -2
  571. package/docs/tools/observe.mdx +6 -6
  572. package/docs/tools/overview.mdx +34 -33
  573. package/docs/tools/query.mdx +5 -5
  574. package/docs/tools/session-and-feedback.mdx +13 -4
  575. package/docs/tools/sessions.mdx +8 -8
  576. package/docs/tools/snapshot.mdx +6 -5
  577. package/docs/tools/state.mdx +9 -9
  578. package/docs/tools/tools-and-run.mdx +26 -24
  579. package/docs/tools/verify.mdx +28 -0
  580. package/docs/tools/wait-for.mdx +8 -4
  581. package/docs/troubleshooting.mdx +21 -6
  582. package/docs/usage.md +51 -45
  583. package/docs/use-cases.mdx +3 -3
  584. package/docs/vs-chrome-devtools-mcp.mdx +9 -6
  585. package/docs/vs-playwright-mcp.mdx +3 -1
  586. package/docs/what-is-recorded.md +7 -5
  587. package/docs/what-is-reticle.mdx +2 -2
  588. package/docs/why-reticle.mdx +1 -1
  589. package/package.json +6 -5
  590. package/dist/command/cli/demo/demo-page.d.ts +0 -78
  591. package/dist/command/cli/demo/demo-page.js +0 -249
  592. package/dist/command/cli/demo/demo-run.d.ts +0 -38
  593. package/dist/command/cli/demo/demo-run.js +0 -138
  594. package/dist/judgement/hunt/hunt-report.d.ts +0 -49
  595. package/dist/judgement/hunt/hunt-report.js +0 -84
  596. package/dist/language/flows/buddy-status.d.ts +0 -25
  597. package/dist/language/flows/buddy-status.js +0 -32
  598. package/dist/language/flows/change/watch-batcher.d.ts +0 -18
  599. package/dist/language/flows/change/watch-batcher.js +0 -19
  600. package/dist/memory/cloud/harness-offer.d.ts +0 -44
  601. package/dist/memory/cloud/harness-offer.js +0 -145
  602. package/dist/surface/tools/feature-capture.d.ts +0 -151
  603. package/dist/surface/tools/feature-capture.js +0 -235
  604. package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
  605. package/dist/surface/tools/tool-hit-rate.js +0 -90
  606. package/dist/telemetry/harness-drive.d.ts +0 -4
  607. package/dist/telemetry/harness-drive.js +0 -35
  608. package/dist/telemetry/identify.d.ts +0 -22
  609. package/dist/telemetry/identify.js +0 -73
  610. package/docs/cli/capsules.mdx +0 -70
  611. package/docs/cli/hunt.mdx +0 -73
  612. package/docs/cli/identify.mdx +0 -76
  613. package/docs/cli/kill.mdx +0 -91
  614. package/docs/cli/watch.mdx +0 -62
@@ -11,25 +11,42 @@
11
11
  * them deterministically with no model in the loop at all.
12
12
  */
13
13
  import { z } from 'zod';
14
+ import { randomUUID } from 'node:crypto';
15
+ import { join } from 'node:path';
14
16
  import { unprovedGoals } from '../../features/harness/goals.js';
15
- import { ReticleTool, asRecord } from '@reticlehq/core';
17
+ import { ReticleEnv, ReticleTool, asRecord } from '@reticlehq/core';
16
18
  import { stepCountSchema } from './args/numeric-bounds.js';
19
+ import { DriveOrigin, DriveRefusedError, DriveStatus, POLL_WAIT_DEFAULT_S, awaitDrive, driveRecord, runningDrive, sayToDrive, startDrive, stillRunning, stopDrive, } from '../../features/harness/drive-runs.js';
20
+ import { fetchPlatformRun, platformRunAnswer, } from '../../features/harness/platform/platform-drives.js';
21
+ import { serverOptionsFromEnv } from '../../features/harness/platform/server-driver.js';
22
+ import { reportPlanBlocked, savedPlanDrivesNothing } from '../../features/harness/platform/script.js';
23
+ import { harnessRunId } from '../../judgement/runs/drive-run.js';
24
+ import { DRIVE_RECORD_SUFFIX, reticleDirPaths } from '../../memory/project/dir/reticle-dir.js';
25
+ import { sessionRoot } from '../../memory/project/session-root.js';
26
+ import { RESULT_FIELD, exploreResult, resultField } from './reticle-result.js';
17
27
  import { runTool } from './invoke-tool.js';
28
+ import { harnessRunSource, reportHarnessBlocked, reportHarnessRun, } from '../../telemetry/harness-run.js';
18
29
  import { driveVerdict } from '../../features/harness/platform/remote-drive.js';
19
- import { exploreApp, harnessAvailable, withLinkedCredential, MSG_NO_HARNESS_KEY, } from './harness-explore.js';
30
+ import { harnessLockRefusal, firstLine, exploreApp, harnessAvailable, withLinkedCredential, MSG_NO_HARNESS_KEY, } from './harness-explore.js';
20
31
  import { EXPLORE_NEEDS } from '../../features/harness/drivers.js';
21
- import { checkTally, describeDrive, replayedFlows } from '../../features/harness/drive-report.js';
32
+ import { checkTally, describeDrive, replayedFlows, runProved, } from '../../features/harness/drive-report.js';
22
33
  import { StopReason } from '../../features/harness/harness.js';
34
+ const isRecord = (v) => 'object' === typeof v && null !== v && !Array.isArray(v);
35
+ export const WAIT_MAX_S = 240;
23
36
  export const EXPLORE_TOOLS = [
24
37
  {
25
38
  name: ReticleTool.VERIFY_EXPLORE,
26
- 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, so the whole drive costs you one tool call instead of a context full of snapshots. 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. Returns { stopReason, steps, savedFlows, summary, usage }. 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. ' +
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. ' +
27
40
  EXPLORE_NEEDS,
28
41
  inputSchema: {
29
42
  persona: z
30
43
  .string()
31
44
  .optional()
32
- .describe('The journey in plain words; any "quoted text" must be on the page when it ends (checked).'),
45
+ .describe('The journey in plain words; "quoted text" must show at the end. Outcome goes in `expect`.'),
46
+ expect: z
47
+ .record(z.string(), z.unknown())
48
+ .optional()
49
+ .describe('Predicate it must end in (route, net, state), asserted after.'),
33
50
  maxSteps: stepCountSchema
34
51
  .optional()
35
52
  .describe('Ceiling on model turns; the drive is graded however it ends.'),
@@ -37,110 +54,373 @@ export const EXPLORE_TOOLS = [
37
54
  .string()
38
55
  .optional()
39
56
  .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.
60
+ wait: z.number().min(0).max(WAIT_MAX_S).optional().describe('Seconds; default 50.'),
61
+ runId: z.string().optional(),
62
+ 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(),
40
65
  },
41
66
  // Everything the handler returns has to be declared, or a schema-aware client never sees it.
67
+ // Optional throughout: a drive still running answers only its status, id and progress.
42
68
  outputSchema: {
43
- stopReason: z.string(),
69
+ /** running | done | stopped | broken. */
70
+ status: z.string().optional(),
71
+ /** The drive's run (`harness-<uuid>`), known from the moment it starts. Poll with it. */
72
+ runId: z.string().optional(),
73
+ /** The drive's latest step or narrated line, while it runs. */
74
+ lastLine: z.string().optional(),
75
+ /** The call that reads this drive again. */
76
+ next: z.object({ action: z.string(), runId: z.string() }).optional(),
77
+ /** Seconds since the drive started, while it runs. */
78
+ elapsedS: z.number().optional(),
79
+ /** While it runs: poll again with the same call; stopping never stops the drive. */
80
+ instruction: z.string().optional(),
81
+ stopReason: z.string().optional(),
44
82
  /** Which driver actually drove, so a comparison can never misattribute its own result. */
45
- driver: z.string(),
46
- steps: z.number(),
47
- savedFlows: z.array(z.string()),
83
+ driver: z.string().optional(),
84
+ steps: z.number().optional(),
85
+ savedFlows: z.array(z.string()).optional(),
48
86
  /** Flows that already existed and were driven and written again. A second run's ordinary result. */
49
- rewroteFlows: z.array(z.string()),
87
+ rewroteFlows: z.array(z.string()).optional(),
50
88
  /** Saved flows with no step that asserts anything: their replay verifies nothing. */
51
- unverifiedFlows: z.array(z.string()),
89
+ unverifiedFlows: z.array(z.string()).optional(),
52
90
  /** Whether the drive ran at least one check. A drive that did not proved nothing. */
53
- proved: z.boolean(),
91
+ proved: z.boolean().optional(),
54
92
  /**
55
93
  * Whether every journey reached its goal, as the platform judged when the drive finished.
56
94
  * Absent when no journey's goal was judged. Checks that held are not the goal reached.
57
95
  */
58
96
  goalMet: z.boolean().optional(),
59
97
  /** How the drive's checks came out, one per control and claim at its worst. */
60
- checks: z.object({ held: z.number(), failed: z.number(), undecided: z.number() }),
98
+ checks: z.object({ held: z.number(), failed: z.number(), undecided: z.number() }).optional(),
61
99
  /** The runs this drive syncs as (`harness-<uuid>`): what the platform links its check to. */
62
100
  runIds: z.array(z.string()).optional(),
101
+ /** The run's page on the dashboard, when the project is linked. Give it to the user. */
102
+ runUrl: z.string().optional(),
103
+ /** The saved plan this run drove: the dashboard keeps it, edits it and runs it again. */
104
+ planId: z.string().optional(),
105
+ planUrl: z.string().optional(),
106
+ /** The credits the platform says this run spent. */
107
+ creditsSpent: z.number().optional(),
63
108
  /** One verdict per requested goal, checked by the harness itself. Only `yes` is proved. */
64
- goals: z.array(z.object({ text: z.string(), verified: z.string() })),
109
+ goals: z
110
+ .array(z.object({ text: z.string(), verified: z.string(), reason: z.string().optional() }))
111
+ .optional(),
65
112
  /**
66
113
  * What the drive set out to do, read from `.reticle` BEFORE it started — every recorded
67
114
  * journey with the consequence that must still hold, and the declared intent nobody has
68
115
  * tested. Returned so the caller can see the drive was aimed rather than wandering.
69
116
  */
70
- plan: z.object({ summary: z.string(), steps: z.array(z.unknown()) }),
117
+ plan: z.object({ summary: z.string(), steps: z.array(z.unknown()) }).optional(),
71
118
  /**
72
119
  * What the drive did, derived from the calls it made and the verdicts the engine returned.
73
120
  *
74
121
  * Not the driver's narration. A driver that cannot write a sentence used to leave this empty,
75
122
  * and a driver that can is the one witness with a reason to round "unknown" up to "worked".
76
123
  */
77
- summary: z.string(),
124
+ summary: z.string().optional(),
125
+ /**
126
+ * The driver's own account, alone. The platform's driver says here why a drive checked nothing
127
+ * at its end, which a caller that only shows the verdict otherwise cannot tell anybody.
128
+ */
129
+ driverAccount: z.string().optional(),
78
130
  /** Present when the drive broke: a model that would not answer, a wedged browser. */
79
131
  error: z.string().optional(),
80
132
  /** What the drive cost, cache hits included, so an expensive run is visible rather than felt. */
81
- usage: z.object({
133
+ usage: z
134
+ .object({
82
135
  input: z.number(),
83
136
  output: z.number(),
84
137
  cacheRead: z.number(),
85
138
  cacheWrite: z.number(),
86
- }),
139
+ })
140
+ .optional(),
87
141
  /** Said out loud when a drive recorded nothing, because that is not a verified app. */
88
142
  note: z.string().optional(),
143
+ /**
144
+ * Each journey of a platform plan and how it ended. Only `passed` is proved. `by`: its saved
145
+ * flows replayed (no model) or the model drove it; `tokens`: what the model spent on it.
146
+ */
147
+ [RESULT_FIELD]: z.string().optional(),
148
+ journeys: z
149
+ .array(z.object({
150
+ title: z.string(),
151
+ status: z.string(),
152
+ by: z.string().optional(),
153
+ tokens: z.number().optional(),
154
+ }))
155
+ .optional(),
89
156
  },
90
- handler: async (deps, args) => {
91
- // The credential `reticle link` already filed counts as configured, so somebody who has
92
- // signed in and linked does not also have to export a key by hand.
93
- const env = await withLinkedCredential(deps, process.env);
94
- if (!harnessAvailable(env))
95
- throw new Error(MSG_NO_HARNESS_KEY);
96
- const persona = args['persona'];
97
- const maxSteps = args['maxSteps'];
98
- const sessionId = args['sessionId'];
99
- const { drive, savedFlows, rewroteFlows, unverifiedFlows, driverName, plan, goals, planLines, runIds, } = await exploreApp(deps, env, {
100
- ...('string' === typeof persona ? { focus: persona } : {}),
101
- ...('number' === typeof maxSteps ? { maxSteps } : {}),
102
- ...('string' === typeof sessionId ? { sessionId } : {}),
103
- });
104
- return {
105
- stopReason: drive.stopReason,
106
- driver: driverName,
107
- steps: drive.steps,
108
- savedFlows: [...savedFlows],
109
- rewroteFlows: [...rewroteFlows],
110
- unverifiedFlows: [...unverifiedFlows],
111
- proved: drive.proved,
112
- checks: checkTally(drive.toolCalls),
113
- ...(drive.goalMet === undefined ? {} : { goalMet: drive.goalMet }),
114
- ...(runIds === undefined ? {} : { runIds: [...runIds] }),
115
- goals: [...goals],
116
- plan: { summary: plan.summary, steps: [...plan.steps] },
117
- // Derived, not narrated. The driver's own `summary` is appended only when it said
118
- // something — it is the one part of this a model authored, so it goes last and is labelled.
119
- summary: [
120
- ...(planLines ?? []),
121
- describeDrive(drive.toolCalls, [...savedFlows, ...rewroteFlows], unverifiedFlows),
122
- ...[unprovedGoals(goals)].filter((line) => line !== undefined),
123
- ...(0 === drive.summary.length ? [] : [`The driver's own account: ${drive.summary}`]),
124
- ].join('\n'),
125
- ...(drive.error === undefined ? {} : { error: drive.error }),
126
- usage: drive.usage,
127
- // The note only fires when the drive left NOTHING behind. A rewritten flow is a flow: it
128
- // replays, it proves what it asserts, and telling its author that "nothing will replay" is
129
- // a lie this tool used to tell on every second run.
130
- /*
131
- * The note fires only when the run left NOTHING behind — and a run that REPLAYED left
132
- * plenty. Sixteen recorded journeys re-proved for zero model tokens is the cheap half of
133
- * the plan doing its job, and telling its author to "raise maxSteps" reads as a failure.
134
- */
135
- ...(0 === savedFlows.length &&
136
- 0 === rewroteFlows.length &&
137
- 0 === replayedFlows(drive.toolCalls).length
138
- ? { note: `${NOTHING_RECORDED[drive.stopReason]}${failureDetail(drive)}` }
139
- : {}),
140
- };
141
- },
157
+ handler: (deps, args, call) => answerExplore(deps, args, call, exploreDrive),
142
158
  },
143
159
  ];
160
+ const MS_PER_S = 1000;
161
+ export const MSG_ALREADY_DRIVING = 'A Harness drive is already running on this app; this is that drive. Poll it by runId; do not start another.';
162
+ const msgNoDrive = (runId) => `No Harness drive ${runId} on this machine, and no linked platform to ask for it.`;
163
+ const str = (v) => ('string' === typeof v ? v : undefined);
164
+ /**
165
+ * Start a drive and wait on it for a while, or poll, stop or speak to one by `runId`.
166
+ *
167
+ * The drive itself never runs inside the call: it is started in the registry and the call waits up
168
+ * to `wait` seconds, so a client's timeout loses nothing. A second start on the same app answers
169
+ * with the drive already running rather than paying for another.
170
+ */
171
+ export async function answerExplore(deps, args, call, drive) {
172
+ const wait = args['wait'];
173
+ const waitMs = MS_PER_S *
174
+ Math.min(WAIT_MAX_S, Math.max(0, 'number' === typeof wait ? wait : POLL_WAIT_DEFAULT_S));
175
+ const sessionId = str(args['sessionId']);
176
+ const asked = str(args['runId']);
177
+ if (asked !== undefined) {
178
+ if (true === args['stop'])
179
+ stopDrive(asked);
180
+ const say = str(args['say']);
181
+ if (say !== undefined)
182
+ sayToDrive(asked, say);
183
+ return answerDrive(deps, asked, waitMs, call, sessionId, false);
184
+ }
185
+ const running = runningDrive(sessionId);
186
+ if (running !== undefined) {
187
+ const answer = await answerDrive(deps, running.harnessRun, waitMs, call, sessionId, false);
188
+ return { ...asRecord(answer), note: MSG_ALREADY_DRIVING };
189
+ }
190
+ // The credential `reticle link` already filed counts as configured, so somebody who has
191
+ // signed in and linked does not also have to export a key by hand.
192
+ const driveId = args['driveId'];
193
+ const env = {
194
+ // The driven tab's project: the plan it saves and the runs it syncs land in one place.
195
+ ...(await withLinkedCredential(deps, process.env, { sessionId })),
196
+ // Billed to the one free drive the platform granted, never to another.
197
+ ...('string' === typeof driveId ? { [ReticleEnv.DRIVE_ID]: driveId } : {}),
198
+ };
199
+ // The coverage gate first: it is local, and the one thing to fix before anything else matters.
200
+ // Not for a saved plan that only replays flows (no model turn to guard), and a plan it refuses
201
+ // is told so, so the plan's history records the attempt.
202
+ const planId = str(args['planId']);
203
+ const platform = serverOptionsFromEnv(env);
204
+ const replayOnly = planId !== undefined &&
205
+ platform !== undefined &&
206
+ (await savedPlanDrivesNothing(platform, planId));
207
+ // In-process callers name their origin. Over MCP nothing can (the schema does not carry it), and
208
+ // a granted free drive with no origin is `reticle try`'s: the HUD's own grant says HUD.
209
+ const origin = Object.values(DriveOrigin).find((o) => o === args['origin']) ??
210
+ ('string' === typeof driveId ? DriveOrigin.TRY : DriveOrigin.AGENT);
211
+ // Hosted: the platform drives (the only driver this build ships), as opposed to an injected one.
212
+ const runStart = {
213
+ source: harnessRunSource(origin, planId),
214
+ hosted: platform !== undefined,
215
+ };
216
+ const locked = replayOnly ? undefined : await harnessLockRefusal(deps, sessionId, env);
217
+ if (locked !== undefined)
218
+ reportHarnessBlocked(runStart);
219
+ if (locked !== undefined) {
220
+ if (planId !== undefined && platform !== undefined)
221
+ await reportPlanBlocked(platform, planId, firstLine(locked));
222
+ throw new Error(locked);
223
+ }
224
+ if (!harnessAvailable(env)) {
225
+ reportHarnessBlocked(runStart);
226
+ throw new Error(MSG_NO_HARNESS_KEY);
227
+ }
228
+ const harness = randomUUID();
229
+ const runId = harnessRunId(harness);
230
+ if (runId === undefined)
231
+ throw new Error(`could not name a run for drive ${harness}`);
232
+ startDrive({
233
+ harness,
234
+ runId,
235
+ origin,
236
+ ...(sessionId === undefined ? {} : { sessionId }),
237
+ now: deps.now,
238
+ persist: (record) => writeDriveFile(deps, sessionId, record),
239
+ run: (control) => reportHarnessRun(runStart, () => drive(deps, env, args, harness, control), (error) => error instanceof DriveRefusedError),
240
+ });
241
+ // A drive refused before it began (switched off, unentitled) is still a refusal to the caller.
242
+ return answerDrive(deps, runId, waitMs, call, sessionId, true);
243
+ }
244
+ /** Wait, then answer with the drive as it stands: running, its full result, or why it broke. */
245
+ async function answerDrive(deps, runId, waitMs, call, sessionId, refuseBroken) {
246
+ const progress = call?.progress;
247
+ await awaitDrive(runId, waitMs, call?.signal, progress === undefined ? undefined : (r) => progress(r.steps, r.lastLine));
248
+ const record = driveRecord(runId) ?? (await readDriveFile(deps, sessionId, runId));
249
+ if (record === undefined)
250
+ return fromPlatform(deps, runId);
251
+ if (DriveStatus.RUNNING === record.status) {
252
+ const elapsedS = Math.max(0, Math.round((deps.now() - record.startedAt) / MS_PER_S));
253
+ return {
254
+ status: record.status,
255
+ runId,
256
+ steps: record.steps,
257
+ lastLine: record.lastLine,
258
+ elapsedS,
259
+ next: { action: 'explore', runId },
260
+ instruction: stillRunning(runId, record.steps, elapsedS),
261
+ };
262
+ }
263
+ if (record.result !== undefined)
264
+ return { status: record.status, runId, ...record.result };
265
+ if (refuseBroken && record.error !== undefined)
266
+ throw new Error(record.error);
267
+ return { status: record.status, runId, steps: record.steps, error: record.error ?? 'no result' };
268
+ }
269
+ /** A run this machine no longer holds, from the platform it synced to. */
270
+ async function fromPlatform(deps, runId) {
271
+ const platform = serverOptionsFromEnv(await withLinkedCredential(deps, process.env));
272
+ if (platform === undefined)
273
+ throw new Error(msgNoDrive(runId));
274
+ const got = await fetchPlatformRun(platform, runId);
275
+ if ('error' in got)
276
+ throw new Error(got.error);
277
+ return platformRunAnswer(runId, got.run);
278
+ }
279
+ /** Where the drive's record lives: its session's own project, never wherever the daemon started. */
280
+ function driveFile(deps, sessionId, runId) {
281
+ return join(reticleDirPaths(sessionRoot(deps, sessionId)).runs, `${runId}${DRIVE_RECORD_SUFFIX}`);
282
+ }
283
+ /** `.reticle/runs/<run>.drive.json`: a new session or a restarted daemon can still read the drive. */
284
+ async function writeDriveFile(deps, sessionId, record) {
285
+ const path = driveFile(deps, sessionId, record.harnessRun);
286
+ await deps.fs.mkdir(join(path, '..'));
287
+ await deps.fs.writeFile(path, `${JSON.stringify(record, null, 2)}\n`);
288
+ }
289
+ async function readDriveFile(deps, sessionId, runId) {
290
+ try {
291
+ const parsed = asRecord(JSON.parse(await deps.fs.readFile(driveFile(deps, sessionId, runId))));
292
+ const status = Object.values(DriveStatus).find((s) => s === parsed['status']);
293
+ if (status === undefined || runId !== parsed['harnessRun'])
294
+ return undefined;
295
+ // A record left `running` by a daemon that has since gone will never finish.
296
+ const lost = DriveStatus.RUNNING === status;
297
+ return {
298
+ ...parsed,
299
+ status: lost ? DriveStatus.BROKEN : status,
300
+ ...(lost ? { error: MSG_DRIVE_LOST } : {}),
301
+ };
302
+ }
303
+ catch {
304
+ return undefined;
305
+ }
306
+ }
307
+ const MSG_DRIVE_LOST = 'The daemon that ran this drive stopped before it finished; what it drove up to then is kept.';
308
+ /** How a drive ended, as the registry records it. */
309
+ function statusOf(stopReason) {
310
+ if (StopReason.STOPPED === stopReason)
311
+ return DriveStatus.STOPPED;
312
+ if (StopReason.BROKEN === stopReason)
313
+ return DriveStatus.BROKEN;
314
+ return DriveStatus.DONE;
315
+ }
316
+ /** The shipped drive: `exploreApp`, steered by the registry, reported as the tool's answer. */
317
+ const exploreDrive = async (deps, env, args, harness, control) => {
318
+ const persona = args['persona'];
319
+ // Only a run a person started may carry the acknowledgement; an agent's drive keeps the guard.
320
+ const confirmDangerous = true === args['confirmDangerous'] &&
321
+ DriveOrigin.AGENT !==
322
+ (Object.values(DriveOrigin).find((o) => o === args['origin']) ?? DriveOrigin.AGENT);
323
+ const maxSteps = args['maxSteps'];
324
+ const sessionId = args['sessionId'];
325
+ const expect = args['expect'];
326
+ const planId = args['planId'];
327
+ const explored = await exploreApp(deps, env, {
328
+ ...('string' === typeof planId ? { planId } : {}),
329
+ ...(confirmDangerous ? { confirmDangerous } : {}),
330
+ ...('string' === typeof persona ? { focus: persona } : {}),
331
+ ...('number' === typeof maxSteps ? { maxSteps } : {}),
332
+ ...('string' === typeof sessionId ? { sessionId } : {}),
333
+ ...(isRecord(expect) ? { expect } : {}),
334
+ harnessId: harness,
335
+ stopped: control.stopped,
336
+ inbox: control.takeSaid,
337
+ });
338
+ const result = exploreReport(explored);
339
+ return {
340
+ status: statusOf(explored.drive.stopReason),
341
+ result: {
342
+ ...result,
343
+ ...resultField(() => exploreResult(sessionRoot(deps, 'string' === typeof sessionId ? sessionId : undefined), {
344
+ checks: checkTally(explored.drive.toolCalls),
345
+ goals: explored.goals,
346
+ ...(explored.drive.goalMet === undefined ? {} : { goalMet: explored.drive.goalMet }),
347
+ ...(explored.journeys === undefined ? {} : { journeys: explored.journeys }),
348
+ ...(explored.runIds === undefined ? {} : { runIds: explored.runIds }),
349
+ })),
350
+ },
351
+ };
352
+ };
353
+ /** The tool's answer for a finished drive. */
354
+ export function exploreReport(explored) {
355
+ const { drive, savedFlows, rewroteFlows, unverifiedFlows, driverName, plan, goals, planLines, runIds, runUrl, journeys, planId, planUrl, disparity, } = explored;
356
+ const checks = checkTally(drive.toolCalls);
357
+ return {
358
+ stopReason: drive.stopReason,
359
+ driver: driverName,
360
+ steps: drive.steps,
361
+ savedFlows: [...savedFlows],
362
+ rewroteFlows: [...rewroteFlows],
363
+ unverifiedFlows: [...unverifiedFlows],
364
+ // The headline's own word, so the answer never says NOT PROVED beside `proved: true`.
365
+ proved: runProved(checks, drive.goalMet, journeys),
366
+ checks,
367
+ ...(drive.goalMet === undefined ? {} : { goalMet: drive.goalMet }),
368
+ ...(runIds === undefined ? {} : { runIds: [...runIds] }),
369
+ // Where the user sees this run, and what it cost: the two things an agent is asked after a run.
370
+ ...(runUrl === undefined ? {} : { runUrl }),
371
+ ...(planId === undefined ? {} : { planId }),
372
+ ...(planUrl === undefined ? {} : { planUrl }),
373
+ ...(drive.creditsSpent === undefined ? {} : { creditsSpent: drive.creditsSpent }),
374
+ ...(journeys === undefined
375
+ ? {}
376
+ : {
377
+ journeys: journeys.map((j) => ({
378
+ title: j.title,
379
+ status: j.status,
380
+ ...(j.by === undefined ? {} : { by: j.by }),
381
+ ...(j.tokens === undefined ? {} : { tokens: j.tokens }),
382
+ })),
383
+ }),
384
+ goals: [...goals],
385
+ // Each goal the agent also judged, compared: agent proved and Harness could not find is a
386
+ // discoverability finding, filed on the platform as its own kind.
387
+ ...(disparity === undefined ? {} : { disparity: [...disparity] }),
388
+ // The local plan only when nothing else planned the drive. Beside a platform plan's journeys it
389
+ // was a second, unrelated plan — fifteen replays of other people's flows — in the same answer.
390
+ ...(journeys === undefined ? { plan: { summary: plan.summary, steps: [...plan.steps] } } : {}),
391
+ // Derived, not narrated. The driver's own account stays out of it, in `driverAccount`: it
392
+ // named flows it never saved ("saved 1 flow(s)" beside "its recording was not saved"), and a
393
+ // summary must never contradict itself.
394
+ summary: [
395
+ ...(planLines ?? []),
396
+ describeDrive(drive.toolCalls, [...savedFlows, ...rewroteFlows], unverifiedFlows, drive.goalMet, journeys),
397
+ ...[unprovedGoals(goals)].filter((line) => line !== undefined),
398
+ ].join('\n'),
399
+ // Not beside a platform plan: its lanes' accounts count saves the platform asked for ("saved 3
400
+ // flow(s)") where `savedFlows` lists what is on disk, and an answer has one count. Each journey's
401
+ // ending is in `journeys`, and the end state in `goals`.
402
+ ...(0 === drive.summary.length || journeys !== undefined
403
+ ? {}
404
+ : { driverAccount: drive.summary }),
405
+ ...(drive.error === undefined ? {} : { error: drive.error }),
406
+ usage: drive.usage,
407
+ // The note only fires when the drive left NOTHING behind. A rewritten flow is a flow: it
408
+ // replays, it proves what it asserts, and telling its author that "nothing will replay" is
409
+ // a lie this tool used to tell on every second run.
410
+ /*
411
+ * The note fires only when the run left NOTHING behind — and a run that REPLAYED left
412
+ * plenty. Sixteen recorded journeys re-proved for zero model tokens is the cheap half of
413
+ * the plan doing its job, and telling its author to "raise maxSteps" reads as a failure.
414
+ */
415
+ // A persona drive's advice: a plan run's journeys already say how each went.
416
+ ...(journeys === undefined &&
417
+ 0 === savedFlows.length &&
418
+ 0 === rewroteFlows.length &&
419
+ 0 === replayedFlows(drive.toolCalls).length
420
+ ? { note: `${NOTHING_RECORDED[drive.stopReason]}${failureDetail(drive)}` }
421
+ : {}),
422
+ };
423
+ }
144
424
  /**
145
425
  * The tool failure behind an empty drive, when there was one.
146
426
  *
@@ -182,6 +462,24 @@ const NOTHING_RECORDED = {
182
462
  [StopReason.BROKEN]: 'The drive broke before saving anything — read `error`. Nothing is proved.',
183
463
  [StopReason.STOPPED]: 'Autonomous driving was switched off before the drive saved anything. Nothing is proved; switch it back on to drive again.',
184
464
  };
465
+ /**
466
+ * The full answer of a drive a start left running: the chat answers once, so it waits for the end.
467
+ * Waited on the registry rather than by polling the tool, which would count every poll as a call.
468
+ */
469
+ async function driveToEnd(out) {
470
+ const runId = out['runId'];
471
+ if (DriveStatus.RUNNING !== out['status'] || 'string' !== typeof runId)
472
+ return out;
473
+ await awaitDrive(runId, undefined);
474
+ const record = driveRecord(runId);
475
+ if (record?.result !== undefined)
476
+ return { status: record.status, runId, ...record.result };
477
+ return {
478
+ status: record?.status,
479
+ runId,
480
+ error: record?.error ?? 'the drive ended with no result',
481
+ };
482
+ }
185
483
  /**
186
484
  * A drive the platform's chat asked for, run as the very tool an agent would call.
187
485
  *
@@ -189,14 +487,19 @@ const NOTHING_RECORDED = {
189
487
  * counted, and the chat's answer is then the same derived summary the agent would have read — never
190
488
  * a model's account of its own drive.
191
489
  */
192
- export async function driveForChat(deps, goal, sessionId) {
490
+ export async function driveForChat(deps, goal, sessionId, planId,
491
+ /** The person who started it acknowledged the saved flows' destructive steps. */
492
+ confirmDangerous) {
193
493
  const explore = EXPLORE_TOOLS.find((tool) => ReticleTool.VERIFY_EXPLORE === tool.name);
194
494
  if (explore === undefined)
195
495
  throw new Error('this build has no Harness drive');
196
- const out = asRecord(await runTool(explore, deps, {
197
- persona: goal,
496
+ const out = await driveToEnd(asRecord(await runTool(explore, deps, {
497
+ // A saved plan carries its own journeys: the request's words are only its label.
498
+ ...(planId === undefined ? { persona: goal } : { planId }),
499
+ origin: DriveOrigin.CHAT,
198
500
  ...(sessionId === undefined ? {} : { sessionId }),
199
- }));
501
+ ...(true === confirmDangerous ? { confirmDangerous } : {}),
502
+ })));
200
503
  const summary = 'string' === typeof out['summary'] ? out['summary'] : '';
201
504
  const error = 'string' === typeof out['error'] ? out['error'] : undefined;
202
505
  const note = 'string' === typeof out['note'] ? out['note'] : undefined;
@@ -20,9 +20,8 @@ declare const FEEDBACK_PROMPT: {
20
20
  * outputSchema, returns the whole verdict block, and its own description calls it "one hop for the
21
21
  * act->observe->assert loop" — it IS the verification path most agents take. The guard that should
22
22
  * have caught the omission keyed on the tool NAME (/assert|verify/), which act_and_wait does not
23
- * match, so it slipped through in silence. Measured over a day of real telemetry: act_and_wait 14
24
- * calls, assert ZERO, verification_completed 2. Agents were verifying the entire time and the metric
25
- * could not see it. The contract test now checks the SHAPE instead.
23
+ * match, so it slipped through in silence: agents verified through act_and_wait the entire time,
24
+ * and the metric could not see it. The contract test now checks the SHAPE instead.
26
25
  */
27
26
  export declare const VERDICT_TOOLS: ReadonlySet<string>;
28
27
  /**
@@ -222,9 +222,8 @@ const FEEDBACK_PROMPT = {
222
222
  * outputSchema, returns the whole verdict block, and its own description calls it "one hop for the
223
223
  * act->observe->assert loop" — it IS the verification path most agents take. The guard that should
224
224
  * have caught the omission keyed on the tool NAME (/assert|verify/), which act_and_wait does not
225
- * match, so it slipped through in silence. Measured over a day of real telemetry: act_and_wait 14
226
- * calls, assert ZERO, verification_completed 2. Agents were verifying the entire time and the metric
227
- * could not see it. The contract test now checks the SHAPE instead.
225
+ * match, so it slipped through in silence: agents verified through act_and_wait the entire time,
226
+ * and the metric could not see it. The contract test now checks the SHAPE instead.
228
227
  */
229
228
  export const VERDICT_TOOLS = new Set([
230
229
  ReticleTool.ACT_AND_WAIT,
@@ -234,6 +233,8 @@ export const VERDICT_TOOLS = new Set([
234
233
  // here rather than six months from now in a metric that had quietly read low the whole time.
235
234
  ReticleTool.ACT_SEQUENCE,
236
235
  ReticleTool.ASSERT,
236
+ // `reticle_assert { action: "wait" }` lands here, and grades its answer as `assert` does.
237
+ ReticleTool.WAIT_FOR,
237
238
  // Merged: `flow_verify` and `verify_change` are actions on this tool now. Naming the merged tool
238
239
  // does NOT make every action a verification — `verificationOf` still requires the result to carry
239
240
  // a `verified` or a pass/fail `status`, so `coverage` and `crawl` contribute nothing and cannot
@@ -31,7 +31,9 @@ interface GapLike {
31
31
  * Returns a NEW array and never mutates its input — the same gap objects travel into telemetry and
32
32
  * into the persisted capsule, and trimming those would lose the remedy where it is read cold.
33
33
  */
34
- export declare function withGapNovelty<T extends GapLike>(sessionId: string | undefined, gaps: readonly T[]): T[];
34
+ export declare function withGapNovelty<T extends GapLike>(sessionId: string | undefined, gaps: readonly T[],
35
+ /** When the verdict was taken: stamps the gaps recorded for the app's coverage. */
36
+ now?: number): T[];
35
37
  /** Forget a session. Called when a session ends so a long-lived daemon does not grow a map forever. */
36
38
  export declare function forgetGapNovelty(sessionId: string): void;
37
39
  /** Test seam: drop every session's memory. */
@@ -20,6 +20,7 @@
20
20
  *
21
21
  * Scoped per session id, because a new session is a new agent that has been told nothing.
22
22
  */
23
+ import { recordGaps } from '../../portal/session/recorded-gaps.js';
23
24
  /** The fields worth repeating. Everything else on a gap is the remedy, said once. */
24
25
  const FACT_KEYS = ['kind', 'ref', 'source'];
25
26
  /** Sessions that have already been given the full text, by gap kind. */
@@ -30,9 +31,15 @@ const toldBySession = new Map();
30
31
  * Returns a NEW array and never mutates its input — the same gap objects travel into telemetry and
31
32
  * into the persisted capsule, and trimming those would lose the remedy where it is read cold.
32
33
  */
33
- export function withGapNovelty(sessionId, gaps) {
34
+ export function withGapNovelty(sessionId, gaps,
35
+ /** When the verdict was taken: stamps the gaps recorded for the app's coverage. */
36
+ now = Date.now()) {
34
37
  if (0 === gaps.length)
35
38
  return [];
39
+ // Before the trim, while each gap still carries its remedy: the app's coverage and the platform
40
+ // report read the full gap, and this is the one place every verdict's gaps pass through.
41
+ if (sessionId !== undefined)
42
+ recordGaps(sessionId, gaps, now);
36
43
  // No session id means no memory to check against, so nothing can be a repeat. Sending the full
37
44
  // text is the safe direction: a reader who has seen it skims, a reader who has not is stuck.
38
45
  if (sessionId === undefined)