@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
@@ -10,210 +10,217 @@ import { loadNamedFlows, resolveChangedFiles } from '../../command/cli/cli-flow-
10
10
  import { projectDirFor } from '../../memory/project/session-root.js';
11
11
  import { affectedSavedFlows } from './change/flow-sources.js';
12
12
  import { FLOW_TOOLS } from './flow-tools.js';
13
+ import { RESULT_FIELD, changeResult } from '../../surface/tools/reticle-result.js';
13
14
  import { findContradictions } from '@reticlehq/engine/disagreement/contradictions.js';
14
15
  import { parseControls } from '../../surface/tools/coverage-tools.js';
15
16
  import { ReticleCommand, SnapshotMode } from '@reticlehq/core';
16
- export const VERIFY_CHANGE_TOOLS = [
17
- {
18
- name: ReticleTool.VERIFY_CHANGE,
19
- description: 'Did my change break anything? Give it the files you edited (or `since` for a git ref) and it works out which saved flows cover them, replays exactly those, and answers with one `verified` (yes|no|unknown) plus `because`. `unknown` when NO saved flow covers the change — that is the honest answer, never a green: nothing ran, so nothing was proved. Returns { verified, because, changedFiles, flowsRun, suite, unknownProvenance }.',
20
- example: { files: ['src/App.tsx'] },
21
- inputSchema: {
22
- files: z
23
- .array(z.string())
24
- .optional()
25
- .describe('Changed file paths, repo-relative (e.g. ["src/App.tsx"]).'),
26
- since: z
27
- .string()
28
- .optional()
29
- .describe('Git ref to diff against (e.g. "HEAD~1", "main"). Unioned with `files`.'),
30
- parallel: workerCountSchema
31
- .optional()
32
- .describe('Replay this many affected flows at once (needs the browser pool).'),
33
- sessionId: z
34
- .string()
35
- .optional()
36
- .describe('OMIT THIS unless you mean a specific tab — Reticle resolves it.'),
37
- },
38
- outputSchema: {
39
- verified: z
40
- .string()
41
- .describe('yes | no | unknown. `unknown` means the evidence could not decide — most often that no saved flow covers these files, which is NOT a pass.'),
42
- because: z.string(),
43
- changedFiles: z.array(z.string()),
44
- flowsRun: z.array(z.string()),
45
- suite: z.unknown().optional(),
46
- // Session-EXEMPT: declare the one-shot human feedback ask or a validating profile strips it.
47
- feedback_prompt: z.unknown().optional(),
48
- unknownProvenance: z.array(z.string()),
49
- contradictions: z
50
- .array(z.unknown())
51
- .optional()
52
- .describe('Findings from channels inspected during replay. Advisory entries are reported without changing the verdict; verdict-deciding entries can turn a green suite into no or unknown. OMITTED when clean; see `measured` when it could not be looked for.'),
53
- untouched: z
54
- .array(z.unknown())
55
- .optional()
56
- .describe('Controls on the page that the covering flows never drove. A green over a small untouched list is stronger evidence than the same green over a large one.'),
57
- measured: z
58
- .array(z.string())
59
- .describe('What was actually looked for. An absent `contradictions` means "none found" ONLY if "contradictions" appears here — otherwise it was never measured.'),
60
- },
61
- handler: async (deps, args) => {
62
- const files = Array.isArray(args['files'])
63
- ? args['files'].filter((f) => 'string' === typeof f)
64
- : [];
65
- const since = asString(args['since']);
66
- // The tree to diff is the SESSION's project, and it is neither of the two things this line
67
- // used to say it was: `deps.reticleRoot` is the daemon's `.reticle` SUBDIRECTORY, and the
68
- // daemon's cwd is wherever the MCP host started it — `/` or `$HOME` for a globally-registered
69
- // server. Both answer about the wrong repository while reporting a clean "nothing changed",
70
- // which reads as a pass. `projectDirFor` is the directory holding the resolved root.
71
- const changed = await resolveChangedFiles(files, since, projectDirFor(deps, asString(args['sessionId'])));
72
- const changedFiles = changed.files;
73
- // Same root flow_save wrote to. A read that resolved differently would report "no flows
74
- // covered this change" over flows that exist, which reads as a clean result rather than
75
- // an error, so the disagreement would be invisible.
76
- // Both halves of the address from one place. The root was already resolved from the session;
77
- // the id used to come from the daemon's cwd, so they named different projects.
78
- const sid = asString(args['sessionId']);
79
- const flows = await loadNamedFlows(deps.fs, sessionRoot(deps, sid), sessionProjectId(deps, sid));
80
- const { affected, unknownProvenance } = affectedSavedFlows(flows, changedFiles);
81
- if (0 === changedFiles.length) {
82
- return {
83
- verified: Verified.UNKNOWN,
84
- // A git failure and a clean tree both arrive here with no files, and they are opposite
85
- // answers: one means there was nothing to decide about, the other means the question was
86
- // never asked. `unknown` is right either way, but the REASON is what the caller acts on.
87
- because: changed.failed
88
- ? `could not read the diff for \`${String(since)}\`, so nothing was compared: ${changed.reason ?? 'git failed'}`
89
- : 'no changed files were given, so there was nothing to decide about',
90
- changedFiles,
91
- flowsRun: [],
92
- unknownProvenance,
93
- // Nothing was looked for, and the schema requires this field to say so. Omitting it made
94
- // the MCP layer reject the whole response, turning an honest UNKNOWN into a protocol error.
95
- measured: [],
96
- };
97
- }
98
- // The guard: an uncovered change is UNKNOWN. Reporting `yes` here would mean "your change is
99
- // fine" on the strength of having run nothing at all.
100
- if (0 === affected.length) {
101
- return {
102
- verified: Verified.UNKNOWN,
103
- /*
104
- * The way out named here has to be one the CALLER can take.
105
- *
106
- * This used to say "record one with reticle_record { action: start } then
107
- * reticle_flow_save". Neither is advertised on the default surface, and that surface has
108
- * no dispatch hatch — so the honest answer to "nothing covers this change" ended in two
109
- * calls that come back "Tool not found". Driving is reachable from every surface, and a
110
- * drive is captured as a flow automatically at teardown, so it is also the instruction
111
- * that makes the NEXT change to these files replay instead of drive.
112
- */
113
- because: `no saved flow covers ${1 === changedFiles.length ? 'this file' : 'these files'} — nothing ran, so nothing is proved. Drive it and prove it directly; the drive is saved as a flow, so the next change here replays instead`,
114
- changedFiles,
115
- flowsRun: [],
116
- unknownProvenance,
117
- // No flow ran, so nothing was measured — said explicitly rather than left undefined.
118
- measured: [],
119
- };
120
- }
121
- const provenanceNote = unknownProvenance.length > 0
122
- ? ` (${String(unknownProvenance.length)} of them re-run only because Reticle cannot tell which sources they cover)`
123
- : '';
124
- // Marked BEFORE the replay so the contradiction sweep afterwards reads exactly the window the
125
- // flows produced, and never events from whatever the agent did before calling this.
126
- const cursor = cursorBeforeReplay(deps, args);
127
- const verify = FLOW_TOOLS.find((tool) => tool.name === ReticleTool.FLOW_VERIFY);
128
- if (verify === undefined)
129
- throw new Error('the whole-suite replay tool is not registered');
130
- const suite = asRecord(await verify.handler(deps, {
131
- names: affected,
132
- ...(asNumber(args['parallel']) === undefined ? {} : { parallel: args['parallel'] }),
133
- ...(asString(args['sessionId']) === undefined ? {} : { sessionId: args['sessionId'] }),
134
- }));
135
- const failed = suite.failed ?? 0;
136
- const passed = suite.passed ?? 0;
137
- // `unverifiable` is NOT a failure — the suite ran and proved nothing, which is the same fact
138
- // this tool answers UNKNOWN for an uncovered change. Treating it as a red emitted a bug_found
139
- // that nothing earned. See verify-change-verdict.
140
- const verdict = verdictForSuite(String(suite.status), failed);
141
- // A NO must be EARNED by a failing flow genuinely tied to the changed files. `affectedSavedFlows`
142
- // re-runs any flow whose sources it cannot determine — over-running beats skipping — and those
143
- // land in `unknownProvenance`. Judging the change on them produced a negative verdict whose own
144
- // explanation admitted the evidence was not tied to the file. See attributed-failure.
145
- const failingNames = Array.isArray(suite.failures)
146
- ? suite.failures
147
- .map((f) => 'object' === typeof f && f !== null ? asString(asRecord(f)['flow']) : undefined)
148
- .filter((n) => n !== undefined)
149
- : // No per-failure detail: the failure is somewhere in the set that ran, so ask the question
150
- // of all of them rather than guessing which.
151
- affected;
152
- const attributed = attributedFailures(failingNames, unknownProvenance);
153
- const earnedNo = verdict === Verified.NO && attributed.length > 0;
154
- const reported = verdict === Verified.NO && !earnedNo ? Verified.UNKNOWN : verdict;
155
- if (reported !== Verified.YES) {
156
- return {
157
- verified: reported,
158
- because: earnedNo
159
- ? `${String(failed)} of ${String(suite.total ?? affected.length)} covering flows failed, including ${attributed.join(', ')}, which cover the changed files${provenanceNote}`
160
- : verdict === Verified.NO
161
- ? `flows failed, but Reticle cannot tell which sources any of them cover (${failingNames.join(', ')}) — so this says nothing about the files you changed. Re-record them so their sources are stamped, or read the replay yourself`
162
- : `the covering flows ran but proved nothing (${String(suite.status)}) — they assert no observable consequence, so a green from them would not have meant your change is safe${provenanceNote}`,
163
- changedFiles,
164
- flowsRun: affected,
165
- suite,
166
- unknownProvenance,
167
- measured: ['flows'],
168
- };
169
- }
170
- // The suite went green. That is exactly when the remaining two channels are worth reading:
171
- // a replay that passes while a write fails is the false green this whole product is about.
172
- const extra = await inspectAfterReplay(deps, args, cursor);
173
- const decidingContradictions = extra.contradictions.filter((c) => !isAdvisory(c.kind));
174
- if (decidingContradictions.length > 0) {
175
- const kinds = decidingContradictions.map((c) => c.kind).join(', ');
176
- const earned = attributedFailures(affected, unknownProvenance).length > 0;
177
- return {
178
- verified: earned ? Verified.NO : Verified.UNKNOWN,
179
- because: earned
180
- ? `every covering flow passed, but channels disagreed during the replay (${kinds}) — a green suite over a failed write is the false green worth looking at${provenanceNote}`
181
- : `channels disagreed during the replay (${kinds}), but Reticle cannot tell which sources any of the flows that ran cover (${affected.join(', ')}), so this says nothing about the files you changed. Drive the change directly; the drive is saved as a flow with its sources stamped`,
182
- changedFiles,
183
- flowsRun: affected,
184
- suite,
185
- unknownProvenance,
186
- contradictions: extra.contradictions,
187
- ...(extra.untouched === undefined ? {} : { untouched: extra.untouched }),
188
- measured: extra.measured,
189
- };
190
- }
191
- // The mirror of `earnedNo`: a green from flows that ran only because their sources are unknown
192
- // says nothing about these files either. At least one attributed flow has to have passed.
193
- if (0 === attributedFailures(affected, unknownProvenance).length) {
194
- return {
195
- verified: Verified.UNKNOWN,
196
- because: `the flows that ran passed, but Reticle cannot tell which sources any of them cover (${affected.join(', ')}), so this says nothing about the files you changed. Drive the change directly; the drive is saved as a flow with its sources stamped`,
197
- changedFiles,
198
- flowsRun: affected,
199
- suite,
200
- unknownProvenance,
201
- ...(0 === extra.contradictions.length ? {} : { contradictions: extra.contradictions }),
202
- measured: extra.measured,
203
- };
204
- }
17
+ const VERIFY_CHANGE = {
18
+ name: ReticleTool.VERIFY_CHANGE,
19
+ description: 'Did my change break anything? Give it the files you edited (or `since` for a git ref) and it works out which saved flows cover them, replays exactly those, and answers with one `verified` (yes|no|unknown) plus `because`. `unknown` when NO saved flow covers the change — that is the honest answer, never a green: nothing ran, so nothing was proved. Returns { verified, because, changedFiles, flowsRun, suite, unknownProvenance }.',
20
+ example: { files: ['src/App.tsx'] },
21
+ inputSchema: {
22
+ files: z
23
+ .array(z.string())
24
+ .optional()
25
+ .describe('Changed file paths, repo-relative (e.g. ["src/App.tsx"]).'),
26
+ since: z
27
+ .string()
28
+ .optional()
29
+ .describe('Git ref to diff against (e.g. "HEAD~1", "main"). Unioned with `files`.'),
30
+ parallel: workerCountSchema
31
+ .optional()
32
+ .describe('Replay this many affected flows at once (needs the browser pool).'),
33
+ sessionId: z
34
+ .string()
35
+ .optional()
36
+ .describe('OMIT THIS unless you mean a specific tab — Reticle resolves it.'),
37
+ },
38
+ outputSchema: {
39
+ verified: z
40
+ .string()
41
+ .describe('yes | no | unknown. `unknown` means the evidence could not decide — most often that no saved flow covers these files, which is NOT a pass.'),
42
+ because: z.string(),
43
+ changedFiles: z.array(z.string()),
44
+ flowsRun: z.array(z.string()),
45
+ suite: z.unknown().optional(),
46
+ // Session-EXEMPT: declare the one-shot human feedback ask or a validating profile strips it.
47
+ feedback_prompt: z.unknown().optional(),
48
+ unknownProvenance: z.array(z.string()),
49
+ contradictions: z
50
+ .array(z.unknown())
51
+ .optional()
52
+ .describe('Findings from channels inspected during replay. Advisory entries are reported without changing the verdict; verdict-deciding entries can turn a green suite into no or unknown. OMITTED when clean; see `measured` when it could not be looked for.'),
53
+ untouched: z
54
+ .array(z.unknown())
55
+ .optional()
56
+ .describe('Controls on the page that the covering flows never drove. A green over a small untouched list is stronger evidence than the same green over a large one.'),
57
+ [RESULT_FIELD]: z.string().optional(),
58
+ measured: z
59
+ .array(z.string())
60
+ .describe('What was actually looked for. An absent `contradictions` means "none found" ONLY if "contradictions" appears here — otherwise it was never measured.'),
61
+ },
62
+ handler: async (deps, args) => {
63
+ const files = Array.isArray(args['files'])
64
+ ? args['files'].filter((f) => 'string' === typeof f)
65
+ : [];
66
+ const since = asString(args['since']);
67
+ // The tree to diff is the SESSION's project, and it is neither of the two things this line
68
+ // used to say it was: `deps.reticleRoot` is the daemon's `.reticle` SUBDIRECTORY, and the
69
+ // daemon's cwd is wherever the MCP host started it — `/` or `$HOME` for a globally-registered
70
+ // server. Both answer about the wrong repository while reporting a clean "nothing changed",
71
+ // which reads as a pass. `projectDirFor` is the directory holding the resolved root.
72
+ const changed = await resolveChangedFiles(files, since, projectDirFor(deps, asString(args['sessionId'])));
73
+ const changedFiles = changed.files;
74
+ // Same root flow_save wrote to. A read that resolved differently would report "no flows
75
+ // covered this change" over flows that exist, which reads as a clean result rather than
76
+ // an error, so the disagreement would be invisible.
77
+ // Both halves of the address from one place. The root was already resolved from the session;
78
+ // the id used to come from the daemon's cwd, so they named different projects.
79
+ const sid = asString(args['sessionId']);
80
+ const flows = await loadNamedFlows(deps.fs, sessionRoot(deps, sid), sessionProjectId(deps, sid));
81
+ const { affected, unknownProvenance } = affectedSavedFlows(flows, changedFiles);
82
+ if (0 === changedFiles.length) {
83
+ return {
84
+ verified: Verified.UNKNOWN,
85
+ // A git failure and a clean tree both arrive here with no files, and they are opposite
86
+ // answers: one means there was nothing to decide about, the other means the question was
87
+ // never asked. `unknown` is right either way, but the REASON is what the caller acts on.
88
+ because: changed.failed
89
+ ? `could not read the diff for \`${String(since)}\`, so nothing was compared: ${changed.reason ?? 'git failed'}`
90
+ : 'no changed files were given, so there was nothing to decide about',
91
+ changedFiles,
92
+ flowsRun: [],
93
+ unknownProvenance,
94
+ // Nothing was looked for, and the schema requires this field to say so. Omitting it made
95
+ // the MCP layer reject the whole response, turning an honest UNKNOWN into a protocol error.
96
+ measured: [],
97
+ };
98
+ }
99
+ // The guard: an uncovered change is UNKNOWN. Reporting `yes` here would mean "your change is
100
+ // fine" on the strength of having run nothing at all.
101
+ if (0 === affected.length) {
205
102
  return {
206
- verified: Verified.YES,
207
- because: `all ${String(passed)} flows covering these files passed${provenanceNote}${extra.untouched === undefined ? '' : `, with ${String(extra.untouched.length)} controls left undriven`}`,
103
+ verified: Verified.UNKNOWN,
104
+ /*
105
+ * The way out named here has to be one the CALLER can take.
106
+ *
107
+ * This used to say "record one with reticle_record { action: start } then
108
+ * reticle_flow_save". Neither is advertised on the default surface, and that surface has
109
+ * no dispatch hatch — so the honest answer to "nothing covers this change" ended in two
110
+ * calls that come back "Tool not found". Driving is reachable from every surface, and a
111
+ * drive is captured as a flow automatically at teardown, so it is also the instruction
112
+ * that makes the NEXT change to these files replay instead of drive.
113
+ */
114
+ because: `no saved flow covers ${1 === changedFiles.length ? 'this file' : 'these files'} — nothing ran, so nothing is proved. Drive it and prove it directly; the drive is saved as a flow, so the next change here replays instead`,
115
+ changedFiles,
116
+ flowsRun: [],
117
+ unknownProvenance,
118
+ // No flow ran, so nothing was measured — said explicitly rather than left undefined.
119
+ measured: [],
120
+ };
121
+ }
122
+ const provenanceNote = unknownProvenance.length > 0
123
+ ? ` (${String(unknownProvenance.length)} of them re-run only because Reticle cannot tell which sources they cover)`
124
+ : '';
125
+ // Marked BEFORE the replay so the contradiction sweep afterwards reads exactly the window the
126
+ // flows produced, and never events from whatever the agent did before calling this.
127
+ const cursor = cursorBeforeReplay(deps, args);
128
+ const verify = FLOW_TOOLS.find((tool) => tool.name === ReticleTool.FLOW_VERIFY);
129
+ if (verify === undefined)
130
+ throw new Error('the whole-suite replay tool is not registered');
131
+ const suite = asRecord(await verify.handler(deps, {
132
+ names: affected,
133
+ ...(asNumber(args['parallel']) === undefined ? {} : { parallel: args['parallel'] }),
134
+ ...(asString(args['sessionId']) === undefined ? {} : { sessionId: args['sessionId'] }),
135
+ }));
136
+ const failed = suite.failed ?? 0;
137
+ const passed = suite.passed ?? 0;
138
+ // `unverifiable` is NOT a failure — the suite ran and proved nothing, which is the same fact
139
+ // this tool answers UNKNOWN for an uncovered change. Treating it as a red emitted a bug_found
140
+ // that nothing earned. See verify-change-verdict.
141
+ const verdict = verdictForSuite(String(suite.status), failed);
142
+ // A NO must be EARNED by a failing flow genuinely tied to the changed files. `affectedSavedFlows`
143
+ // re-runs any flow whose sources it cannot determine — over-running beats skipping — and those
144
+ // land in `unknownProvenance`. Judging the change on them produced a negative verdict whose own
145
+ // explanation admitted the evidence was not tied to the file. See attributed-failure.
146
+ const failingNames = Array.isArray(suite.failures)
147
+ ? suite.failures
148
+ .map((f) => 'object' === typeof f && f !== null ? asString(asRecord(f)['flow']) : undefined)
149
+ .filter((n) => n !== undefined)
150
+ : // No per-failure detail: the failure is somewhere in the set that ran, so ask the question
151
+ // of all of them rather than guessing which.
152
+ affected;
153
+ const attributed = attributedFailures(failingNames, unknownProvenance);
154
+ const earnedNo = verdict === Verified.NO && attributed.length > 0;
155
+ const reported = verdict === Verified.NO && !earnedNo ? Verified.UNKNOWN : verdict;
156
+ if (reported !== Verified.YES) {
157
+ return {
158
+ verified: reported,
159
+ because: earnedNo
160
+ ? `${String(failed)} of ${String(suite.total ?? affected.length)} covering flows failed, including ${attributed.join(', ')}, which cover the changed files${provenanceNote}`
161
+ : verdict === Verified.NO
162
+ ? `flows failed, but Reticle cannot tell which sources any of them cover (${failingNames.join(', ')}) — so this says nothing about the files you changed. Re-record them so their sources are stamped, or read the replay yourself`
163
+ : `the covering flows ran but proved nothing (${String(suite.status)}) — they assert no observable consequence, so a green from them would not have meant your change is safe${provenanceNote}`,
208
164
  changedFiles,
209
165
  flowsRun: affected,
210
166
  suite,
211
167
  unknownProvenance,
212
- ...(0 === extra.contradictions.length ? {} : { contradictions: extra.contradictions }),
168
+ measured: ['flows'],
169
+ };
170
+ }
171
+ // The suite went green. That is exactly when the remaining two channels are worth reading:
172
+ // a replay that passes while a write fails is the false green this whole product is about.
173
+ const extra = await inspectAfterReplay(deps, args, cursor);
174
+ const decidingContradictions = extra.contradictions.filter((c) => !isAdvisory(c.kind));
175
+ if (decidingContradictions.length > 0) {
176
+ const kinds = decidingContradictions.map((c) => c.kind).join(', ');
177
+ const earned = attributedFailures(affected, unknownProvenance).length > 0;
178
+ return {
179
+ verified: earned ? Verified.NO : Verified.UNKNOWN,
180
+ because: earned
181
+ ? `every covering flow passed, but channels disagreed during the replay (${kinds}) — a green suite over a failed write is the false green worth looking at${provenanceNote}`
182
+ : `channels disagreed during the replay (${kinds}), but Reticle cannot tell which sources any of the flows that ran cover (${affected.join(', ')}), so this says nothing about the files you changed. Drive the change directly; the drive is saved as a flow with its sources stamped`,
183
+ changedFiles,
184
+ flowsRun: affected,
185
+ suite,
186
+ unknownProvenance,
187
+ contradictions: extra.contradictions,
213
188
  ...(extra.untouched === undefined ? {} : { untouched: extra.untouched }),
214
189
  measured: extra.measured,
215
190
  };
216
- },
191
+ }
192
+ // The mirror of `earnedNo`: a green from flows that ran only because their sources are unknown
193
+ // says nothing about these files either. At least one attributed flow has to have passed.
194
+ if (0 === attributedFailures(affected, unknownProvenance).length) {
195
+ return {
196
+ verified: Verified.UNKNOWN,
197
+ because: `the flows that ran passed, but Reticle cannot tell which sources any of them cover (${affected.join(', ')}), so this says nothing about the files you changed. Drive the change directly; the drive is saved as a flow with its sources stamped`,
198
+ changedFiles,
199
+ flowsRun: affected,
200
+ suite,
201
+ unknownProvenance,
202
+ ...(0 === extra.contradictions.length ? {} : { contradictions: extra.contradictions }),
203
+ measured: extra.measured,
204
+ };
205
+ }
206
+ return {
207
+ verified: Verified.YES,
208
+ because: `all ${String(passed)} flows covering these files passed${provenanceNote}${extra.untouched === undefined ? '' : `, with ${String(extra.untouched.length)} controls left undriven`}`,
209
+ changedFiles,
210
+ flowsRun: affected,
211
+ suite,
212
+ unknownProvenance,
213
+ ...(0 === extra.contradictions.length ? {} : { contradictions: extra.contradictions }),
214
+ ...(extra.untouched === undefined ? {} : { untouched: extra.untouched }),
215
+ measured: extra.measured,
216
+ };
217
+ },
218
+ };
219
+ /** Every answer carries the Reticle result block, under the verdict this tool decided. */
220
+ export const VERIFY_CHANGE_TOOLS = [
221
+ {
222
+ ...VERIFY_CHANGE,
223
+ handler: async (deps, args, call) => changeResult(sessionRoot(deps, asString(args['sessionId'])), await VERIFY_CHANGE.handler(deps, args, call)),
217
224
  },
218
225
  ];
219
226
  /** The event cursor to sweep from, or 0 when there is no readable session (nothing to sweep). */
@@ -26,6 +26,11 @@ interface SyncPolicy {
26
26
  * the bug happen again, and a defect nobody else can reproduce is a defect nobody else can fix.
27
27
  */
28
28
  capsules: boolean;
29
+ /**
30
+ * Push each run's step screenshots after the run. Follows `runs` when the link does not say:
31
+ * a project that keeps its runs local has not agreed to send pictures of its pages either.
32
+ */
33
+ screenshots: boolean;
29
34
  }
30
35
  /** Where a verification actually executes. Reserved for the hosted-runner path; default local. */
31
36
  export declare const VerifyMode: {
@@ -40,8 +45,12 @@ export interface ProjectCloud {
40
45
  verify: VerifyMode;
41
46
  /** The cloud project id this project is linked to (for display/logging); null when env-fallback. */
42
47
  projectId: string | null;
48
+ /** The link as one key, cloud + workspace + project (`credentialSlot`); absent when env-fallback. */
49
+ link?: string;
43
50
  /** Why `config` is null, in words a person can act on. Absent when attached. */
44
51
  reason?: string;
52
+ /** Attached, but not with the key the caller exported: said, never silent. */
53
+ note?: string;
45
54
  }
46
55
  /** Look up the API key for a cloud project id in the user credentials map. */
47
56
  /**
@@ -77,7 +86,7 @@ export declare function resolveProjectCloud(fs: FileSystemPort, reticleRoot: str
77
86
  * three lines somewhere they belong. The harness reads it so a machine that has run `reticle link`
78
87
  * needs no exported key — see `withLinkedCredential`.
79
88
  */
80
- export declare const linkedCloudPort: (fs: FileSystemPort, reticleRoot: string, homeDir: string, env: NodeJS.ProcessEnv) => () => Promise<CloudConfig | null>;
89
+ export declare const linkedCloudPort: (fs: FileSystemPort, reticleRoot: string, homeDir: string, env: NodeJS.ProcessEnv) => (root?: string) => Promise<CloudConfig | null>;
81
90
  /**
82
91
  * The API key a tab's own project resolves to, so a credential is only ever used for its own tabs.
83
92
  * A tab whose project cannot be resolved has no key.
@@ -17,7 +17,13 @@ import { resolveCloudConfig } from './cloud-sync.js';
17
17
  export const CLOUD_LINK_FILE = ReticleDir.CLOUD_LINK_FILE;
18
18
  /** `~/.reticle/credentials.json` — the user's per-cloud-project API keys (the only secret). */
19
19
  export const CREDENTIALS_FILE = 'credentials.json';
20
- const DEFAULT_SYNC_POLICY = { runs: true, memory: true, flows: true, capsules: true };
20
+ const DEFAULT_SYNC_POLICY = {
21
+ runs: true,
22
+ memory: true,
23
+ flows: true,
24
+ capsules: true,
25
+ screenshots: true,
26
+ };
21
27
  /** Where a verification actually executes. Reserved for the hosted-runner path; default local. */
22
28
  export const VerifyMode = { LOCAL: 'local', SERVER: 'server' };
23
29
  const asBool = (v, fallback) => ('boolean' === typeof v ? v : fallback);
@@ -51,6 +57,7 @@ function parseLink(raw) {
51
57
  memory: asBool(sync.memory, DEFAULT_SYNC_POLICY.memory),
52
58
  flows: asBool(sync.flows, DEFAULT_SYNC_POLICY.flows),
53
59
  capsules: asBool(sync.capsules, DEFAULT_SYNC_POLICY.capsules),
60
+ screenshots: asBool(sync.screenshots, asBool(sync.runs, DEFAULT_SYNC_POLICY.screenshots)),
54
61
  },
55
62
  verify: o.verify === VerifyMode.SERVER ? VerifyMode.SERVER : VerifyMode.LOCAL,
56
63
  };
@@ -135,7 +142,7 @@ export async function resolveProjectCloud(fs, reticleRoot, homeDir, env) {
135
142
  verify: VerifyMode.LOCAL,
136
143
  projectId: null,
137
144
  ...(null === config
138
- ? { reason: 'cloud not attached here: run `reticle link`, or set RETICLE_API_KEY' }
145
+ ? { reason: 'cloud not attached here: run `reticle connect`, or set RETICLE_API_KEY' }
139
146
  : {}),
140
147
  };
141
148
  }
@@ -144,6 +151,7 @@ export async function resolveProjectCloud(fs, reticleRoot, homeDir, env) {
144
151
  memory: link.sync.memory ?? DEFAULT_SYNC_POLICY.memory,
145
152
  flows: link.sync.flows ?? DEFAULT_SYNC_POLICY.flows,
146
153
  capsules: link.sync.capsules ?? DEFAULT_SYNC_POLICY.capsules,
154
+ screenshots: link.sync.screenshots ?? DEFAULT_SYNC_POLICY.screenshots,
147
155
  };
148
156
  const stored = credentialFor(await readJson(fs, join(homeDir, ReticleDir.ROOT, CREDENTIALS_FILE)), link.projectId, link.url, link.orgId);
149
157
  /*
@@ -161,16 +169,22 @@ export async function resolveProjectCloud(fs, reticleRoot, homeDir, env) {
161
169
  ? undefined
162
170
  : fromEnv !== null
163
171
  ? `this repo is linked to ${linkedUrl}, and RETICLE_API_KEY is set for ${fromEnv.url}: ` +
164
- `set RETICLE_CLOUD_URL=${linkedUrl} to use that key here, or run \`reticle login\` for it`
172
+ `set RETICLE_CLOUD_URL=${linkedUrl} to use that key here, or run \`reticle connect\` for it`
165
173
  : `this repo is linked to ${linkedUrl}, and this machine has no key for it: run ` +
166
- '`reticle login`, or set RETICLE_API_KEY' +
174
+ '`reticle connect`, or set RETICLE_API_KEY' +
167
175
  (DEFAULT_PLATFORM_URL === linkedUrl ? '' : ` with RETICLE_CLOUD_URL=${linkedUrl}`);
176
+ const note = stored !== null && fromEnv !== null && fromEnv.apiKey !== stored
177
+ ? `RETICLE_API_KEY is set, but this repo uses the key \`reticle connect\` stored for it: ` +
178
+ 'a stored key wins over the environment, so the exported key was not used'
179
+ : undefined;
168
180
  return {
169
181
  config,
170
182
  policy,
171
183
  verify: link.verify,
172
184
  projectId: link.projectId,
185
+ link: credentialSlot(link.url, link.projectId, link.orgId),
173
186
  ...(reason === undefined ? {} : { reason }),
187
+ ...(note === undefined ? {} : { note }),
174
188
  };
175
189
  }
176
190
  /**
@@ -181,7 +195,7 @@ export async function resolveProjectCloud(fs, reticleRoot, homeDir, env) {
181
195
  * three lines somewhere they belong. The harness reads it so a machine that has run `reticle link`
182
196
  * needs no exported key — see `withLinkedCredential`.
183
197
  */
184
- export const linkedCloudPort = (fs, reticleRoot, homeDir, env) => async () => (await resolveProjectCloud(fs, reticleRoot, homeDir, env)).config;
198
+ export const linkedCloudPort = (fs, reticleRoot, homeDir, env) => async (root) => (await resolveProjectCloud(fs, root ?? reticleRoot, homeDir, env)).config;
185
199
  /**
186
200
  * The API key a tab's own project resolves to, so a credential is only ever used for its own tabs.
187
201
  * A tab whose project cannot be resolved has no key.
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { shareableRun } from '@reticlehq/core/artifacts';
12
12
  import { z } from 'zod';
13
- import { VERIFY_PROGRESS_MAX_EVENTS, platformCredentialFrom, } from '@reticlehq/core';
13
+ import { VERIFY_PROGRESS_MAX_EVENTS, platformCredentialFrom, flowJsonReplacer, } from '@reticlehq/core';
14
14
  /**
15
15
  * Env var names — the presence of BOTH is what "logged in" means for cloud sync.
16
16
  *
@@ -61,7 +61,7 @@ const TIMEOUT_ERROR_NAMES = new Set(['TimeoutError', 'AbortError']);
61
61
  const UNSENDABLE_HEADER_CHAR = /[^ -ÿ]/;
62
62
  /** Which knob holds the credential behind a header, so the error can name the thing to go and edit. */
63
63
  const CREDENTIAL_SOURCE = {
64
- authorization: `${CloudEnv.KEY} (or the session from \`reticle login\`)`,
64
+ authorization: `${CloudEnv.KEY} (or the session from \`reticle connect\`)`,
65
65
  };
66
66
  /**
67
67
  * Refuse a request whose headers cannot be encoded, before it is sent.
@@ -126,7 +126,7 @@ export async function syncFlowToCloud(flow, config, projectId, fetchImpl) {
126
126
  'content-type': 'application/json',
127
127
  authorization: `Bearer ${config.apiKey}`,
128
128
  },
129
- body: JSON.stringify(projectId === undefined ? { flow } : { flow, projectId }),
129
+ body: JSON.stringify(projectId === undefined ? { flow } : { flow, projectId }, flowJsonReplacer),
130
130
  });
131
131
  return res.ok
132
132
  ? { outcome: SyncOutcome.SYNCED, status: res.status }
@@ -1,3 +1,24 @@
1
+ /**
2
+ * The harness switch, cached for a HUD that reads it inline.
3
+ *
4
+ * `fetchPlatformConfig` already asks the platform and parses the answer; this adds the one thing the
5
+ * impact snapshot needs and the drive path does not — a SYNCHRONOUS read. The snapshot is rebuilt
6
+ * dozens of times a session, the answer changes about as often as somebody opens a settings page,
7
+ * and a panel that awaited the network on every repaint would be a panel that stutters.
8
+ *
9
+ * Deliberately its own cache rather than a shared abstraction with the notices source:
10
+ * two callers with the same caching problem is not yet a pattern, and the day one of them needs a
11
+ * different staleness rule the shared version becomes the awkward one.
12
+ *
13
+ * Every failure answers `undefined`, which the HUD must read as "we have not heard" and render as NO
14
+ * control — never as "off". Showing a switch that cannot be honoured is worse than showing none.
15
+ *
16
+ * The loader arrives as a PORT rather than an import. Reading the platform lives in
17
+ * `features/harness`, and a cloud-memory file reaching into a feature is the reach the directory
18
+ * guard refuses — correctly, and for the same reason it refused the tool surface reaching the other
19
+ * way. Whoever wires the daemon owns both sides and can hand one to the other.
20
+ */
21
+ import type { CreditKind } from '@reticlehq/core';
1
22
  /** Narrowed to what this file stores: the caller's own type is richer and irrelevant here. */
2
23
  export interface HarnessConfigView {
3
24
  provider: string;
@@ -8,8 +29,36 @@ export interface HarnessConfigView {
8
29
  credits?: {
9
30
  used: number;
10
31
  limit: number;
32
+ kind?: CreditKind | undefined;
33
+ endsAt?: number;
34
+ };
35
+ /** The platform's coverage gate, when it sent one: the HUD prefers it to the local score. */
36
+ gate?: {
37
+ unlocked: boolean;
38
+ percent?: number;
39
+ reason?: string;
40
+ prompt?: string;
11
41
  };
42
+ plan?: {
43
+ kind: CreditKind;
44
+ daysLeft?: number;
45
+ seats?: number;
46
+ };
47
+ harnessRuns?: number;
48
+ memoryEntries?: number;
12
49
  }
50
+ /**
51
+ * The answer as the HUD is pushed it: the grant's end date becomes whole days left, counted on the
52
+ * daemon's clock (the HUD has none worth trusting for this). No end date, no days. Pure.
53
+ */
54
+ export declare function configForHud(config: HarnessConfigView, now: number): Omit<HarnessConfigView, 'credits'> & {
55
+ credits?: {
56
+ used: number;
57
+ limit: number;
58
+ kind?: CreditKind | undefined;
59
+ daysLeft?: number;
60
+ };
61
+ };
13
62
  export interface ConfigSource {
14
63
  read(): HarnessConfigView | undefined;
15
64
  }