@reticlehq/server 3.3.0 → 3.5.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 (326) hide show
  1. package/LICENSE-ENTERPRISE +1 -1
  2. package/README.md +3 -3
  3. package/SKILL.md +12 -14
  4. package/dist/chromium-launch-options.d.ts +4 -3
  5. package/dist/chromium-launch-options.js +6 -2
  6. package/dist/command/cli/adhoc-suite.d.ts +22 -0
  7. package/dist/command/cli/adhoc-suite.js +90 -19
  8. package/dist/command/cli/adhoc-verdict.d.ts +35 -0
  9. package/dist/command/cli/adhoc-verdict.js +90 -3
  10. package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
  11. package/dist/command/cli/answers/gate-hook-message.js +6 -0
  12. package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
  13. package/dist/command/cli/auth/cloud-keystore.js +0 -3
  14. package/dist/command/cli/auth/cloud-session.d.ts +6 -2
  15. package/dist/command/cli/auth/cloud-session.js +2 -0
  16. package/dist/command/cli/auth/device-flow.d.ts +2 -2
  17. package/dist/command/cli/cli-doctor.js +7 -5
  18. package/dist/command/cli/cli-flow-commands.d.ts +3 -6
  19. package/dist/command/cli/cli-flow-commands.js +67 -4
  20. package/dist/command/cli/cli-parse.d.ts +8 -1
  21. package/dist/command/cli/cli-parse.js +24 -4
  22. package/dist/command/cli/cli-update-commands.js +8 -1
  23. package/dist/command/cli/cli-verify.d.ts +24 -3
  24. package/dist/command/cli/cli-verify.js +54 -11
  25. package/dist/command/cli/cloud-cli.js +64 -2
  26. package/dist/command/cli/cloud-kit.js +3 -5
  27. package/dist/command/cli/cloud-login.d.ts +1 -1
  28. package/dist/command/cli/cloud-login.js +27 -8
  29. package/dist/command/cli/demo/demo-page.js +6 -3
  30. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
  31. package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
  32. package/dist/command/cli/launch/cli-launch.d.ts +8 -0
  33. package/dist/command/cli/launch/cli-launch.js +21 -9
  34. package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
  35. package/dist/command/cli/launch/sdk-version-match.js +206 -0
  36. package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
  37. package/dist/command/cli/ports/resolve/cli-port.d.ts +25 -0
  38. package/dist/command/cli/ports/resolve/cli-port.js +125 -15
  39. package/dist/command/cli/setup-mcp-cli.js +21 -13
  40. package/dist/command/cli/tutorial.d.ts +2 -2
  41. package/dist/command/cli/tutorial.js +21 -7
  42. package/dist/command/cli.js +79 -11
  43. package/dist/command/daemon/binding/daemon-status-probe.d.ts +14 -2
  44. package/dist/command/daemon/binding/daemon-status-probe.js +34 -6
  45. package/dist/command/daemon/daemon-resolve.d.ts +9 -0
  46. package/dist/command/daemon/daemon-resolve.js +1 -1
  47. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
  48. package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
  49. package/dist/command/dev/stale-issue-guard.d.ts +3 -4
  50. package/dist/command/dev/stale-issue-guard.js +8 -8
  51. package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
  52. package/dist/command/setup/bringup/connect-lease.js +38 -0
  53. package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
  54. package/dist/command/setup/bringup/dev-server-files.js +131 -0
  55. package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
  56. package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
  57. package/dist/command/setup/bringup/dev-server-wait.d.ts +2 -0
  58. package/dist/command/setup/bringup/dev-server-wait.js +1 -1
  59. package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
  60. package/dist/command/setup/bringup/ensure-daemon.js +135 -16
  61. package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
  62. package/dist/command/setup/bringup/static-page-server.js +151 -0
  63. package/dist/command/setup/desktop-shape.d.ts +4 -0
  64. package/dist/command/setup/desktop-shape.js +18 -2
  65. package/dist/command/setup/init/init-port.d.ts +15 -0
  66. package/dist/command/setup/init/init-port.js +44 -0
  67. package/dist/command/setup/init/init-runtime.d.ts +1 -1
  68. package/dist/command/setup/init/init-runtime.js +91 -9
  69. package/dist/command/setup/node-effects.d.ts +27 -14
  70. package/dist/command/setup/node-effects.js +86 -45
  71. package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
  72. package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
  73. package/dist/command/setup/probe/page-probe.d.ts +13 -2
  74. package/dist/command/setup/probe/page-probe.js +18 -3
  75. package/dist/command/setup/run-setup.d.ts +28 -1
  76. package/dist/command/setup/run-setup.js +103 -15
  77. package/dist/command/setup/session-pick.d.ts +13 -0
  78. package/dist/command/setup/session-pick.js +20 -0
  79. package/dist/command/setup/setup-command.d.ts +2 -14
  80. package/dist/command/setup/setup-command.js +105 -31
  81. package/dist/command/setup/setup-mcp.d.ts +2 -0
  82. package/dist/command/setup/setup-mcp.js +17 -8
  83. package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
  84. package/dist/command/setup/terminal/wait-progress.js +17 -0
  85. package/dist/command/version/peer-announce.d.ts +6 -2
  86. package/dist/command/version/peer-announce.js +8 -6
  87. package/dist/command/version/version-nudge.d.ts +17 -3
  88. package/dist/command/version/version-nudge.js +25 -4
  89. package/dist/features/crawl/crawl-empty.js +1 -1
  90. package/dist/features/crawl/crawl-tools.d.ts +6 -0
  91. package/dist/features/crawl/crawl-tools.js +57 -0
  92. package/dist/features/crawl/crawl.js +171 -157
  93. package/dist/features/exhaust/app-coverage.d.ts +33 -0
  94. package/dist/features/exhaust/app-coverage.js +31 -0
  95. package/dist/features/exhaust/code-coverage.d.ts +47 -0
  96. package/dist/features/exhaust/code-coverage.js +77 -0
  97. package/dist/features/exhaust/exhaust-run.d.ts +33 -0
  98. package/dist/features/exhaust/exhaust-run.js +82 -0
  99. package/dist/features/exhaust/explorer.d.ts +103 -0
  100. package/dist/features/exhaust/explorer.js +195 -0
  101. package/dist/features/exhaust/failure-branches.d.ts +33 -0
  102. package/dist/features/exhaust/failure-branches.js +92 -0
  103. package/dist/features/exhaust/ledger.d.ts +202 -0
  104. package/dist/features/exhaust/ledger.js +252 -0
  105. package/dist/features/exhaust/session-fold.d.ts +31 -0
  106. package/dist/features/exhaust/session-fold.js +79 -0
  107. package/dist/features/exhaust/session-port.d.ts +36 -0
  108. package/dist/features/exhaust/session-port.js +102 -0
  109. package/dist/features/harness/drive-report.d.ts +0 -9
  110. package/dist/features/harness/drive-report.js +18 -0
  111. package/dist/features/harness/driver.js +4 -7
  112. package/dist/features/harness/fill-values.d.ts +10 -0
  113. package/dist/features/harness/fill-values.js +50 -11
  114. package/dist/features/harness/jev-driver.js +30 -13
  115. package/dist/features/harness/openai-driver.d.ts +12 -0
  116. package/dist/features/harness/openai-driver.js +21 -12
  117. package/dist/features/harness/platform-config.js +5 -6
  118. package/dist/features/license/license.d.ts +2 -2
  119. package/dist/index.d.ts +9 -0
  120. package/dist/index.js +56 -19
  121. package/dist/judgement/capsule/capsule-store.d.ts +4 -4
  122. package/dist/judgement/domain/domain-model.js +3 -1
  123. package/dist/judgement/oracles/self-instrument.js +4 -2
  124. package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
  125. package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
  126. package/dist/judgement/runs/verification-sync.d.ts +3 -1
  127. package/dist/judgement/runs/verification-sync.js +1 -1
  128. package/dist/language/flows/change/affected.d.ts +9 -0
  129. package/dist/language/flows/change/affected.js +35 -0
  130. package/dist/language/flows/change/gate.d.ts +20 -0
  131. package/dist/language/flows/change/gate.js +12 -1
  132. package/dist/language/flows/flow-author.d.ts +7 -0
  133. package/dist/language/flows/flow-author.js +19 -0
  134. package/dist/language/flows/flow-expect-grammar.js +1 -1
  135. package/dist/language/flows/flow-journey.d.ts +54 -0
  136. package/dist/language/flows/flow-journey.js +119 -0
  137. package/dist/language/flows/flow-replay-run.d.ts +2 -3
  138. package/dist/language/flows/flow-replay-run.js +13 -47
  139. package/dist/language/flows/flow-replay.d.ts +22 -8
  140. package/dist/language/flows/flow-replay.js +55 -9
  141. package/dist/language/flows/flow-result.d.ts +2 -0
  142. package/dist/language/flows/flow-result.js +25 -0
  143. package/dist/language/flows/flow-tools.js +17 -5
  144. package/dist/language/flows/flows.d.ts +10 -1
  145. package/dist/language/flows/flows.js +90 -14
  146. package/dist/language/flows/recording/replay-halt.js +3 -1
  147. package/dist/language/flows/recording/tape/recordings.d.ts +30 -1
  148. package/dist/language/flows/recording/tape/recordings.js +55 -1
  149. package/dist/language/flows/replay.d.ts +19 -0
  150. package/dist/language/flows/replay.js +42 -6
  151. package/dist/language/flows/verify-change-tools.js +5 -2
  152. package/dist/launch-chromium.d.ts +80 -0
  153. package/dist/launch-chromium.js +218 -0
  154. package/dist/memory/cloud/cloud-config.d.ts +11 -2
  155. package/dist/memory/cloud/cloud-config.js +47 -7
  156. package/dist/memory/cloud/cloud-sync.d.ts +4 -1
  157. package/dist/memory/cloud/cloud-sync.js +6 -9
  158. package/dist/memory/cloud/harness-offer.js +5 -6
  159. package/dist/memory/cloud/harness-switch.js +5 -6
  160. package/dist/memory/cloud/sync-cycle.d.ts +77 -3
  161. package/dist/memory/cloud/sync-cycle.js +325 -55
  162. package/dist/memory/cloud/sync-daemon.js +24 -20
  163. package/dist/memory/cloud/sync-disk.js +16 -2
  164. package/dist/memory/intent/intent-shard.d.ts +10 -32
  165. package/dist/memory/intent/intent-shard.js +0 -10
  166. package/dist/memory/intent/intent-tools.js +8 -3
  167. package/dist/memory/journal/drive-flow.d.ts +18 -13
  168. package/dist/memory/journal/drive-flow.js +45 -37
  169. package/dist/memory/journal/envelope.d.ts +8 -8
  170. package/dist/memory/journal/journal-query.d.ts +14 -0
  171. package/dist/memory/journal/journal-query.js +15 -0
  172. package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
  173. package/dist/memory/journal/session-end.d.ts +9 -1
  174. package/dist/memory/journal/session-end.js +24 -12
  175. package/dist/memory/project/artifact-root-resolver.d.ts +10 -0
  176. package/dist/memory/project/artifact-root-resolver.js +46 -19
  177. package/dist/memory/project/dir/reticle-dir.d.ts +2 -0
  178. package/dist/memory/project/dir/reticle-dir.js +1 -0
  179. package/dist/memory/project/project-tools.js +3 -1
  180. package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
  181. package/dist/portal/bridge/auth-failure-reason.js +4 -2
  182. package/dist/portal/bridge/bridge.d.ts +12 -3
  183. package/dist/portal/bridge/bridge.js +49 -3
  184. package/dist/portal/input/js-coverage.d.ts +25 -0
  185. package/dist/portal/input/js-coverage.js +30 -0
  186. package/dist/portal/input/real-input.d.ts +11 -0
  187. package/dist/portal/input/real-input.js +29 -4
  188. package/dist/portal/input/viewport-tools.js +16 -6
  189. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  190. package/dist/portal/pool/playwright-launcher.js +20 -13
  191. package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
  192. package/dist/portal/session/dev-server/dev-command.js +30 -12
  193. package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
  194. package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
  195. package/dist/portal/session/dev-server/route-status-probe.js +57 -0
  196. package/dist/portal/session/facts/observed-state.d.ts +11 -0
  197. package/dist/portal/session/facts/observed-state.js +34 -0
  198. package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
  199. package/dist/portal/session/no-session-diagnosis.js +62 -5
  200. package/dist/portal/session/no-session-next-action.d.ts +13 -0
  201. package/dist/portal/session/no-session-next-action.js +48 -9
  202. package/dist/portal/session/no-session-watch.d.ts +18 -0
  203. package/dist/portal/session/no-session-watch.js +88 -2
  204. package/dist/portal/session/session-health.js +3 -2
  205. package/dist/portal/session/session-manager.d.ts +20 -6
  206. package/dist/portal/session/session-manager.js +19 -3
  207. package/dist/portal/session/session-reaper.d.ts +28 -0
  208. package/dist/portal/session/session-reaper.js +49 -1
  209. package/dist/portal/session/session.d.ts +10 -8
  210. package/dist/portal/session/session.js +24 -32
  211. package/dist/surface/http-server.d.ts +3 -2
  212. package/dist/surface/http-server.js +8 -5
  213. package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
  214. package/dist/surface/mcp/mcp-proxy.js +2 -2
  215. package/dist/surface/mcp/mcp.js +5 -3
  216. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
  217. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
  218. package/dist/surface/mcp/proxy/proxy-log.js +3 -5
  219. package/dist/surface/mcp/server-instructions.js +9 -7
  220. package/dist/surface/tools/act/after-match.d.ts +17 -0
  221. package/dist/surface/tools/act/after-match.js +30 -0
  222. package/dist/surface/tools/act/durable.d.ts +69 -0
  223. package/dist/surface/tools/act/durable.js +139 -0
  224. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +13 -6
  225. package/dist/surface/tools/act/navigation/navigate-arrival.js +47 -10
  226. package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
  227. package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
  228. package/dist/surface/tools/act/proved-controls.d.ts +9 -0
  229. package/dist/surface/tools/act/proved-controls.js +11 -0
  230. package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
  231. package/dist/surface/tools/act/sequence-grade.js +32 -1
  232. package/dist/surface/tools/act-capsule.d.ts +7 -0
  233. package/dist/surface/tools/act-capsule.js +46 -1
  234. package/dist/surface/tools/act-sequence-tool.js +25 -8
  235. package/dist/surface/tools/act-tools.js +28 -52
  236. package/dist/surface/tools/assert/assert-grade.js +12 -1
  237. package/dist/surface/tools/assert/assert-verdict.js +2 -2
  238. package/dist/surface/tools/browser-tools.js +14 -2
  239. package/dist/surface/tools/coverage-tools.js +27 -1
  240. package/dist/surface/tools/dynamic-tools.js +29 -2
  241. package/dist/surface/tools/error-recovery.js +5 -6
  242. package/dist/surface/tools/feedback-invite.js +6 -4
  243. package/dist/surface/tools/harness-explore.d.ts +3 -2
  244. package/dist/surface/tools/harness-explore.js +12 -7
  245. package/dist/surface/tools/invoke-tool.js +12 -8
  246. package/dist/surface/tools/lease-tools.js +15 -6
  247. package/dist/surface/tools/live-call-text.js +4 -1
  248. package/dist/surface/tools/observe-tools.js +9 -1
  249. package/dist/surface/tools/tool-kit.d.ts +10 -0
  250. package/dist/surface/tools/tools.js +4 -0
  251. package/dist/telemetry/argument-shape.d.ts +0 -2
  252. package/dist/telemetry/argument-shape.js +1 -5
  253. package/dist/telemetry/error-class.d.ts +2 -3
  254. package/dist/telemetry/error-class.js +2 -3
  255. package/dist/telemetry/harness-drive.d.ts +0 -2
  256. package/dist/telemetry/harness-drive.js +0 -4
  257. package/dist/telemetry/onboarding-firsts.d.ts +2 -2
  258. package/dist/telemetry/onboarding-firsts.js +2 -2
  259. package/dist/wire-journal.d.ts +3 -0
  260. package/dist/wire-journal.js +4 -0
  261. package/dist/workspace-packages.d.ts +0 -2
  262. package/dist/workspace-packages.js +1 -9
  263. package/docs/README.md +4 -2
  264. package/docs/actions.mdx +3 -4
  265. package/docs/agent-cheatsheet.md +4 -4
  266. package/docs/architecture.md +6 -6
  267. package/docs/best-practices.mdx +1 -1
  268. package/docs/capabilities.mdx +32 -1
  269. package/docs/cli/cloud.mdx +20 -8
  270. package/docs/cli/doctor.mdx +4 -0
  271. package/docs/cli/feedback.mdx +2 -2
  272. package/docs/cli/gate.mdx +9 -4
  273. package/docs/cli/update.mdx +10 -0
  274. package/docs/cli.mdx +22 -7
  275. package/docs/desktop.mdx +14 -10
  276. package/docs/environment.mdx +87 -0
  277. package/docs/faq.mdx +18 -16
  278. package/docs/first-drive.md +5 -13
  279. package/docs/flows.md +21 -2
  280. package/docs/frameworks.mdx +29 -10
  281. package/docs/getting-started.md +15 -5
  282. package/docs/http-transport.md +6 -3
  283. package/docs/human-control.md +2 -2
  284. package/docs/index.mdx +16 -6
  285. package/docs/install-agentic.mdx +2 -2
  286. package/docs/install-manual.mdx +1 -1
  287. package/docs/integration-patterns.md +3 -3
  288. package/docs/multi-agent-testing.md +1 -1
  289. package/docs/packages/babel-plugin.mdx +1 -1
  290. package/docs/packages/browser.mdx +2 -2
  291. package/docs/packages/next.mdx +3 -0
  292. package/docs/packages/react.mdx +2 -2
  293. package/docs/packages/tauri.mdx +1 -1
  294. package/docs/packages/vite-plugin.mdx +4 -2
  295. package/docs/packages.mdx +9 -9
  296. package/docs/predicates.mdx +50 -5
  297. package/docs/quickstart.mdx +12 -10
  298. package/docs/recipes.mdx +2 -2
  299. package/docs/state-management.mdx +2 -2
  300. package/docs/system-map.md +1 -1
  301. package/docs/telemetry.md +1 -1
  302. package/docs/token-efficiency.md +3 -3
  303. package/docs/tools/act-and-wait.mdx +1 -0
  304. package/docs/tools/act-sequence.mdx +1 -1
  305. package/docs/tools/act.mdx +1 -1
  306. package/docs/tools/console.mdx +1 -1
  307. package/docs/tools/inspect.mdx +1 -1
  308. package/docs/tools/network.mdx +1 -1
  309. package/docs/tools/query.mdx +1 -1
  310. package/docs/tools/sessions.mdx +1 -1
  311. package/docs/tools/snapshot.mdx +1 -1
  312. package/docs/tools/state.mdx +1 -1
  313. package/docs/tools/wait-for.mdx +1 -1
  314. package/docs/usage.md +7 -7
  315. package/docs/use-cases.mdx +157 -0
  316. package/docs/vs-screenshots.mdx +3 -3
  317. package/docs/what-is-recorded.md +2 -2
  318. package/docs/what-is-reticle.mdx +17 -20
  319. package/docs/why-reticle.mdx +14 -21
  320. package/package.json +10 -9
  321. package/dist/judgement/capsule/minimize.d.ts +0 -12
  322. package/dist/judgement/capsule/minimize.js +0 -22
  323. package/dist/judgement/phenomena/phenomena.d.ts +0 -27
  324. package/dist/judgement/phenomena/phenomena.js +0 -101
  325. package/dist/language/flows/flow-report.d.ts +0 -22
  326. package/dist/language/flows/flow-report.js +0 -106
@@ -1,14 +1,15 @@
1
1
  import { asFlowName, asProjectId, } from '@reticlehq/core';
2
2
  import { REDACTED_FILL } from './fields/flow-secret-field.js';
3
- export { REDACTED_FILL } from './fields/flow-secret-field.js';
3
+ export { REDACTED_FILL, secretEnvKey } from './fields/flow-secret-field.js';
4
4
  import { safeProjectId } from './flow-result.js';
5
5
  import { changeInPlace } from './narrow-write.js';
6
- import { AnchorKind, DEGRADED_ANCHOR_ROLE, FLOW_FILE_VERSION, FlowErrorCode, FlowFileSchema, FlowStepTool, QueryBy, defaultIsSensitiveKey, } from '@reticlehq/core';
6
+ import { AnchorKind, DEGRADED_ANCHOR_ROLE, FLOW_FILE_VERSION, flowFileVersionFor, FlowErrorCode, FlowFileSchema, FlowStepTool, QueryBy, defaultIsSensitiveKey, } from '@reticlehq/core';
7
7
  import { ReticleTool } from '@reticlehq/core';
8
8
  import { asRecord, asString } from '@reticlehq/core';
9
9
  import { applyHealChanges } from './heal.js';
10
10
  import { withLearnedSources } from './learned-sources.js';
11
11
  import { flowIntentGap, linkFlowIntent } from './flow-intent.js';
12
+ import { journeyFingerprint, journeyName, mergeJourney, routeClaims } from './flow-journey.js';
12
13
  import { IntentStore } from '../../memory/intent/intent-store.js';
13
14
  import { flowDir, flowParentDir, flowPath, reticleDirPaths, isValidFlowName, } from '../../memory/project/dir/reticle-dir.js';
14
15
  import { describeFlowZodFailure, parseFlowFileText } from './flow-expect-grammar.js';
@@ -164,6 +165,14 @@ function buildStep(tool, anchor, action, args, degraded) {
164
165
  * NEVER silently dropped. ACT_SEQUENCE recurses over its sub-steps.
165
166
  */
166
167
  export function recordedStepToFlowStep(step) {
168
+ const out = anchoredStep(step);
169
+ if (step.page !== undefined)
170
+ out.page = step.page;
171
+ if (step.endPage !== undefined && step.endPage !== step.page)
172
+ out.endPage = step.endPage;
173
+ return out;
174
+ }
175
+ function anchoredStep(step) {
167
176
  if (step.invoke !== undefined) {
168
177
  // An invocation drives nothing: no action, no args, and an anchor only because every step
169
178
  // carries one. Falling through to the action path below would give it an `action` and a
@@ -237,7 +246,8 @@ export class FlowStore {
237
246
  * of them produces byte-identical on-disk content (locked by the byte-stability tests).
238
247
  */
239
248
  #serialize(flow) {
240
- return `${JSON.stringify(flow, null, JSON_INDENT)}\n`;
249
+ const stamped = { ...flow, version: flowFileVersionFor(flow) };
250
+ return `${JSON.stringify(stamped, null, JSON_INDENT)}\n`;
241
251
  }
242
252
  /**
243
253
  * Register the flow's business goal in the intent ledger and stamp the row's id onto the flow.
@@ -267,16 +277,8 @@ export class FlowStore {
267
277
  ...(gap === undefined ? {} : { intentGap: gap }),
268
278
  };
269
279
  }
270
- /**
271
- * Convert a CompiledProgram (testid-normalized) into an anchored, on-disk flow + write it.
272
- * Optionally fold structured annotations (per-step expect, dynamic[], success) onto
273
- * the flow before writing. Omitting `annotations` reproduces the same bytes.
274
- */
275
- async save(program, annotations, projectId) {
276
- if (!isValidFlowName(program.name)) {
277
- return { ok: false, code: FlowErrorCode.INVALID_NAME };
278
- }
279
- const pid = safeProjectId(projectId);
280
+ /** The on-disk flow a compiled recording becomes, before anything is written. */
281
+ #build(program, annotations, pid) {
280
282
  const steps = program.steps.map(recordedStepToFlowStep);
281
283
  const base = {
282
284
  version: FLOW_FILE_VERSION,
@@ -295,11 +297,85 @@ export class FlowStore {
295
297
  */
296
298
  ...(program.startPath === undefined ? {} : { startPath: program.startPath }),
297
299
  };
298
- const flow = await this.#linkIntent(withAnnotations(base, annotations));
300
+ const intent = program.steps.find((step) => step.intent !== undefined)?.intent;
301
+ const annotated = withAnnotations(base, annotations);
302
+ return {
303
+ ...annotated,
304
+ // The action's own intent, when nobody annotated the flow — see RecordedStep.intent.
305
+ ...(annotated.intent === undefined && intent !== undefined ? { intent } : {}),
306
+ ...routeClaims(annotated.startPath, annotated.steps),
307
+ ...(program.author === undefined ? {} : { author: program.author }),
308
+ };
309
+ }
310
+ /**
311
+ * Convert a CompiledProgram (testid-normalized) into an anchored, on-disk flow + write it.
312
+ * Optionally fold structured annotations (per-step expect, dynamic[], success) onto
313
+ * the flow before writing. Omitting `annotations` reproduces the same bytes.
314
+ */
315
+ async save(program, annotations, projectId) {
316
+ if (!isValidFlowName(program.name)) {
317
+ return { ok: false, code: FlowErrorCode.INVALID_NAME };
318
+ }
319
+ const pid = safeProjectId(projectId);
320
+ const base = this.#build(program, annotations, pid);
321
+ const flow = await this.#linkIntent(base);
299
322
  await this.#fs.mkdir(flowParentDir(this.#root, program.name, pid));
300
323
  await this.#fs.writeFile(flowPath(this.#root, program.name, pid), this.#serialize(flow));
301
324
  return { ok: true, value: this.#summary(flow) };
302
325
  }
326
+ /**
327
+ * Save a flow the session drove, folded into any saved copy of the same journey.
328
+ *
329
+ * Every session saves its own drive, so one journey arrived as a near-copy per session. Flows
330
+ * under `prefix` (the auto-saved ones — a hand-named flow is never merged or removed) that start
331
+ * on the same page and take the same steps become ONE file: the oldest keeps its name, the others
332
+ * fill its gaps and are removed. The same scan collapses copies saved before this existed.
333
+ */
334
+ async saveJourney(program, projectId, prefix) {
335
+ if (!isValidFlowName(program.name))
336
+ return { ok: false, code: FlowErrorCode.INVALID_NAME };
337
+ const pid = safeProjectId(projectId);
338
+ const fresh = this.#build(program, undefined, pid);
339
+ const saved = [];
340
+ for (const name of (await this.list(pid)).filter((n) => n.startsWith(prefix))) {
341
+ const loaded = await this.load(name, pid);
342
+ if (loaded.ok)
343
+ saved.push(loaded.value);
344
+ }
345
+ const byPrint = new Map();
346
+ for (const flow of saved) {
347
+ const print = journeyFingerprint(flow);
348
+ byPrint.set(print, [...(byPrint.get(print) ?? []), flow]);
349
+ }
350
+ const print = journeyFingerprint(fresh);
351
+ const taken = new Map(saved.map((flow) => [flow.name, journeyFingerprint(flow)]));
352
+ const named = { ...fresh, name: journeyName(fresh.name, print, taken) };
353
+ byPrint.set(print, [...(byPrint.get(print) ?? []), named]);
354
+ let result = named;
355
+ for (const [groupPrint, group] of byPrint) {
356
+ if (group.length < 2 && groupPrint !== print)
357
+ continue;
358
+ const [kept, ...rest] = [...group].sort((a, b) => a.createdAt - b.createdAt);
359
+ if (kept === undefined)
360
+ continue;
361
+ const merged = await this.#linkIntent(rest.reduce(mergeJourney, kept));
362
+ await this.#writeInPlace(merged, pid);
363
+ for (const dropped of rest) {
364
+ if (dropped.name !== merged.name && dropped !== named)
365
+ await this.remove(dropped.name, pid);
366
+ }
367
+ if (groupPrint === print)
368
+ result = merged;
369
+ }
370
+ return { ok: true, value: this.#summary(result) };
371
+ }
372
+ /** Write a flow where it already lives, or into its project's directory when it is new. */
373
+ async #writeInPlace(flow, pid) {
374
+ const name = asFlowName(flow.name);
375
+ const path = (await this.#resolveReadPath(name, pid)) ?? flowPath(this.#root, name, pid);
376
+ await this.#fs.mkdir(flowParentDir(this.#root, name, pid));
377
+ await this.#fs.writeFile(path, this.#serialize(flow));
378
+ }
303
379
  /**
304
380
  * Persist an already-anchored FlowFile captured in-page (no recompilation). The
305
381
  * browser resolved every semantic anchor at capture time; here we only validate the name +
@@ -21,7 +21,9 @@ export function haltedFrom(results, total) {
21
21
  return undefined;
22
22
  if (last.ok && last.drift === undefined)
23
23
  return undefined;
24
- const notAttempted = total - results.length;
24
+ // From the step's own index, not the count of results: a resumed replay does not report its setup
25
+ // prefix, so the results array no longer starts at step 0.
26
+ const notAttempted = total - (last.step + 1);
25
27
  // A failure on the FINAL step is not a halt: nothing was left to attempt, and `notAttempted: 0`
26
28
  // would send a reader looking for steps that do not exist.
27
29
  if (notAttempted <= 0)
@@ -1,4 +1,4 @@
1
- import { type Predicate } from '@reticlehq/core';
1
+ import { type FlowFile, type Predicate } from '@reticlehq/core';
2
2
  /** One captured agent action, normalized for replay. */
3
3
  export interface RecordedStep {
4
4
  /** ReticleTool.ACT | ReticleTool.ACT_SEQUENCE */
@@ -27,6 +27,12 @@ export interface RecordedStep {
27
27
  * `invoke` first and treats the step as a call.
28
28
  */
29
29
  invoke?: string;
30
+ /** The page this step ran on. Written to the saved step (unlike `route`, which cuts journeys). */
31
+ page?: string;
32
+ /** The page it ended on once it settled — see `markEnded`. */
33
+ endPage?: string;
34
+ /** Why the agent took this step: the `intent` it declared on the action. Names the saved flow. */
35
+ intent?: string;
30
36
  }
31
37
  interface ActiveRecording {
32
38
  cursor: number;
@@ -59,6 +65,8 @@ export interface CompiledProgram {
59
65
  * format change.
60
66
  */
61
67
  routes?: string[];
68
+ /** Who made it — see FlowFile.author. Stamped by the caller that knows. */
69
+ author?: FlowFile['author'];
62
70
  }
63
71
  /**
64
72
  * The `tool` written on an invocation step — core's constant, re-exported for this module's callers.
@@ -105,6 +113,27 @@ export declare class RecordingStore {
105
113
  * anything should not carry an empty tape, and "did anything happen at all" stays answerable.
106
114
  */
107
115
  capture(step: RecordedStep, route?: string): void;
116
+ /**
117
+ * Add a consequence to the last captured step of every active recording.
118
+ *
119
+ * A separate `reticle_assert` after an act is how an agent proves what the act did, and it never
120
+ * reached the recorder — only the act tools called `capture` — so the flow kept the click and lost
121
+ * the proof. What the step already declared is kept; the new check joins it under `allOf`.
122
+ */
123
+ attachExpect(expect: Predicate): void;
124
+ /**
125
+ * A navigation happened. Navigating records no step, so until the next one is captured, a check
126
+ * proved now is about the page the navigation opened, not about the last step — and kept on that
127
+ * step, replay would ask it right after the click, of a page it was never true on.
128
+ */
129
+ markNavigated(): void;
130
+ /**
131
+ * The page the step just captured ended on, once its action settled.
132
+ *
133
+ * Fills only a step that has none, so calling it after an action that captured nothing leaves the
134
+ * previous step's own answer alone.
135
+ */
136
+ markEnded(page: string | undefined): void;
108
137
  /**
109
138
  * Returns the active recording (cursor + steps) and clears it, or undefined if not recording.
110
139
  *
@@ -1,4 +1,4 @@
1
- import { FlowStepTool } from '@reticlehq/core';
1
+ import { FlowStepTool, PredicateKind } from '@reticlehq/core';
2
2
  /**
3
3
  * The `tool` written on an invocation step — core's constant, re-exported for this module's callers.
4
4
  *
@@ -31,6 +31,8 @@ const AMBIENT_STEP_CAP = 400;
31
31
  export class RecordingStore {
32
32
  #active = new Map();
33
33
  #compiled = new Map();
34
+ /** A navigation came after the last captured step — see markNavigated. */
35
+ #navigatedSinceStep = false;
34
36
  start(name, cursor, startPath) {
35
37
  const openedOver = new Map();
36
38
  for (const [outer, rec] of this.#active)
@@ -67,6 +69,7 @@ export class RecordingStore {
67
69
  * anything should not carry an empty tape, and "did anything happen at all" stays answerable.
68
70
  */
69
71
  capture(step, route) {
72
+ this.#navigatedSinceStep = false;
70
73
  if (!this.#active.has(AMBIENT_RECORDING)) {
71
74
  this.#active.set(AMBIENT_RECORDING, {
72
75
  cursor: 0,
@@ -83,12 +86,63 @@ export class RecordingStore {
83
86
  // opened on purpose is not capped — they said when it starts and they say when it stops.
84
87
  if (AMBIENT_RECORDING === name && rec.steps.length >= AMBIENT_STEP_CAP)
85
88
  continue;
89
+ // A step nobody marked ended where the next one began.
90
+ const previous = rec.steps.at(-1);
91
+ if (previous !== undefined && previous.endPage === undefined && step.page !== undefined) {
92
+ previous.endPage = step.page;
93
+ }
86
94
  // The route rides on the AMBIENT tape only: a recording somebody opened deliberately is
87
95
  // already one journey by construction, and stamping a route on its steps would change what a
88
96
  // deliberate recording contains.
89
97
  rec.steps.push(AMBIENT_RECORDING === name && route !== undefined ? { ...step, route } : step);
90
98
  }
91
99
  }
100
+ /**
101
+ * Add a consequence to the last captured step of every active recording.
102
+ *
103
+ * A separate `reticle_assert` after an act is how an agent proves what the act did, and it never
104
+ * reached the recorder — only the act tools called `capture` — so the flow kept the click and lost
105
+ * the proof. What the step already declared is kept; the new check joins it under `allOf`.
106
+ */
107
+ attachExpect(expect) {
108
+ if (this.#navigatedSinceStep)
109
+ return;
110
+ for (const rec of this.#active.values()) {
111
+ const last = rec.steps.at(-1);
112
+ if (last === undefined)
113
+ continue;
114
+ const held = last.expect;
115
+ last.expect =
116
+ held === undefined
117
+ ? expect
118
+ : PredicateKind.ALL_OF === held.kind
119
+ ? { ...held, predicates: [...held.predicates, expect] }
120
+ : { kind: PredicateKind.ALL_OF, predicates: [held, expect] };
121
+ }
122
+ }
123
+ /**
124
+ * A navigation happened. Navigating records no step, so until the next one is captured, a check
125
+ * proved now is about the page the navigation opened, not about the last step — and kept on that
126
+ * step, replay would ask it right after the click, of a page it was never true on.
127
+ */
128
+ markNavigated() {
129
+ this.#navigatedSinceStep = true;
130
+ }
131
+ /**
132
+ * The page the step just captured ended on, once its action settled.
133
+ *
134
+ * Fills only a step that has none, so calling it after an action that captured nothing leaves the
135
+ * previous step's own answer alone.
136
+ */
137
+ markEnded(page) {
138
+ if (page === undefined)
139
+ return;
140
+ for (const rec of this.#active.values()) {
141
+ const last = rec.steps.at(-1);
142
+ if (last !== undefined && last.endPage === undefined)
143
+ last.endPage = page;
144
+ }
145
+ }
92
146
  /**
93
147
  * Returns the active recording (cursor + steps) and clears it, or undefined if not recording.
94
148
  *
@@ -1,3 +1,4 @@
1
+ import { type Predicate } from '@reticlehq/core';
1
2
  import { type CommandResult } from '@reticlehq/core';
2
3
  import type { RecordedStep, CompiledProgram } from './recording/tape/recordings.js';
3
4
  import type { Session } from '../../portal/session/session.js';
@@ -27,6 +28,13 @@ export declare function queryRefs(result: CommandResult): string[];
27
28
  * an empty box fails identically and tells the reader nothing.
28
29
  */
29
30
  export declare function replayActionArgs(value: unknown, confirmDangerous?: boolean, field?: string): Record<string, unknown>;
31
+ /**
32
+ * The pathname of a session's live url, or undefined when it has none yet.
33
+ *
34
+ * Pathname rather than the whole url, because that is what `startPath` is compared against and what
35
+ * a hash-routed app makes meaningless in the document location — see routeOfEvent.
36
+ */
37
+ export declare function pathOf(url: string | undefined): string | undefined;
30
38
  /**
31
39
  * Capture an act into every in-flight recording, or do nothing when none is running.
32
40
  *
@@ -40,6 +48,17 @@ export declare function captureAct(recordings: {
40
48
  }, args: Record<string, unknown>, res: unknown,
41
49
  /** Where this step ran, so the ambient tape can be cut into journeys. See RecordedStep.route. */
42
50
  route?: string): void;
51
+ /**
52
+ * Capture an act_and_wait once its verdict is known, keeping `until` as the step's expectation only
53
+ * on a yes. Recorded before the verdict, a step kept an `until` that came back `no` or `no-fault` —
54
+ * a regression test asserting something never once observed to hold because of the action. The
55
+ * action itself is always recorded: it happened, and replay needs it to reach the next step.
56
+ */
57
+ export declare function captureVerdictedAct(recordings: Parameters<typeof captureAct>[0], args: Record<string, unknown>, res: unknown, route: string | undefined, verified: string | undefined): void;
58
+ /** Fold a PASSING standalone assertion into the step it proved. See RecordingStore.attachExpect. */
59
+ export declare function captureAssertion(recordings: {
60
+ attachExpect: (expect: Predicate) => void;
61
+ }, raw: unknown): void;
43
62
  /**
44
63
  * Compile a single reticle_act invocation into a normalized RecordedStep using the action result. Anchor
45
64
  * priority mirrors synthesizeAnchor: a data-testid is the gold standard; failing that, the element's
@@ -1,5 +1,5 @@
1
1
  import { REDACTED_FILL, secretEnvKey } from './fields/flow-secret-field.js';
2
- import { FlowPredicateSchema } from '@reticlehq/core';
2
+ import { FlowPredicateSchema, Verified } from '@reticlehq/core';
3
3
  import { DANGEROUS_ACTION_CONFIRM_ARG, ReticleCommand, QueryBy, } from '@reticlehq/core';
4
4
  import { ReticleTool } from '@reticlehq/core';
5
5
  import { formatStepAddress } from 'open-verification';
@@ -71,6 +71,22 @@ function sourceFromResult(res) {
71
71
  out['column'] = source['column'];
72
72
  return out;
73
73
  }
74
+ /**
75
+ * The pathname of a session's live url, or undefined when it has none yet.
76
+ *
77
+ * Pathname rather than the whole url, because that is what `startPath` is compared against and what
78
+ * a hash-routed app makes meaningless in the document location — see routeOfEvent.
79
+ */
80
+ export function pathOf(url) {
81
+ if (url === undefined)
82
+ return undefined;
83
+ try {
84
+ return new URL(url).pathname;
85
+ }
86
+ catch {
87
+ return undefined;
88
+ }
89
+ }
74
90
  /**
75
91
  * Capture an act into every in-flight recording, or do nothing when none is running.
76
92
  *
@@ -87,20 +103,40 @@ route) {
87
103
  // — "recording is a property of the system rather than a rule an agent has to remember" — was true
88
104
  // about the intent and false about the code, and every drive nobody wrapped by hand was discarded.
89
105
  //
90
- // That is the whole supply of regression tests this product could have had for free: the corpus
91
- // measures 3 of 33 flows mutation-testable and 6 of 112 steps declaring a consequence, and a drive
92
- // that is never recorded cannot contribute to either.
106
+ // That is the whole supply of regression tests this product could have had for free: few saved
107
+ // flows are mutation-testable and few steps declare a consequence, and a drive that is never
108
+ // recorded cannot contribute to either.
93
109
  const step = compileActStep(args, res);
94
110
  // Keep the assertion the agent actually made. `act_and_wait { until }` IS the agent saying what
95
111
  // success means — the large majority of calls carry one — and dropping it produced a flow graded
96
112
  // "assertion-free: it will pass even if the feature is broken", which is the regression-suite
97
- // story failing at its last step. Only kinds FlowExpect can express survive; see
98
- // predicate-to-expect.ts.
113
+ // story failing at its last step. See enforceableExpect below.
99
114
  const expect = enforceableExpect(args['until'] ?? args['predicate']);
100
115
  if (expect !== undefined)
101
116
  step.expect = expect;
117
+ if (route !== undefined)
118
+ step.page = route;
119
+ const intent = asString(args['intent'])?.trim();
120
+ if (intent !== undefined && intent.length > 0)
121
+ step.intent = intent;
102
122
  recordings.capture(step, route);
103
123
  }
124
+ /**
125
+ * Capture an act_and_wait once its verdict is known, keeping `until` as the step's expectation only
126
+ * on a yes. Recorded before the verdict, a step kept an `until` that came back `no` or `no-fault` —
127
+ * a regression test asserting something never once observed to hold because of the action. The
128
+ * action itself is always recorded: it happened, and replay needs it to reach the next step.
129
+ */
130
+ export function captureVerdictedAct(recordings, args, res, route, verified) {
131
+ const { until: _until, predicate: _predicate, ...unproved } = args;
132
+ captureAct(recordings, Verified.YES === verified ? args : unproved, res, route);
133
+ }
134
+ /** Fold a PASSING standalone assertion into the step it proved. See RecordingStore.attachExpect. */
135
+ export function captureAssertion(recordings, raw) {
136
+ const expect = enforceableExpect(raw);
137
+ if (expect !== undefined)
138
+ recordings.attachExpect(expect);
139
+ }
104
140
  /**
105
141
  * A declared predicate, as a recorded expectation — or nothing.
106
142
  *
@@ -172,9 +172,12 @@ export const VERIFY_CHANGE_TOOLS = [
172
172
  const extra = await inspectAfterReplay(deps, args, cursor);
173
173
  if (extra.contradictions.length > 0) {
174
174
  const kinds = extra.contradictions.map((c) => c.kind).join(', ');
175
+ const earned = attributedFailures(affected, unknownProvenance).length > 0;
175
176
  return {
176
- verified: Verified.NO,
177
- because: `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}`,
177
+ verified: earned ? Verified.NO : Verified.UNKNOWN,
178
+ because: earned
179
+ ? `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}`
180
+ : `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`,
178
181
  changedFiles,
179
182
  flowsRun: affected,
180
183
  suite,
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The ONE place a Reticle-owned Chromium is launched — the pooled lease browser
3
+ * (`playwright-launcher`) and the drive browser (`real-input`) both come through here.
4
+ *
5
+ * Why it is more than `chromium.launch`: on a fresh machine the `playwright` package is present
6
+ * (npx pulled it in with the server) but its browser build is not, because that is a separate
7
+ * ~150 MiB download nobody told the user about. Every lease, `reticle verify`, `open` and `drive`
8
+ * then refused with "Chromium is not installed for Playwright. Run: npx playwright@… install
9
+ * chromium" — the first run stopped on a human step a machine could do. Most machines already have
10
+ * a Chrome or an Edge, and Playwright drives either through `channel`, so that is tried before any
11
+ * download; only a machine with neither gets the pinned install, run for them and announced.
12
+ *
13
+ * Announcements go to STDERR, never stdout: `reticle mcp` speaks JSON-RPC on stdout and one stray
14
+ * line there corrupts the client's stream.
15
+ */
16
+ import { type ChromiumLaunchOptions } from './chromium-launch-options.js';
17
+ import { type ChromiumProbe } from './command/cli/doctor/browser/chromium-hint.js';
18
+ /** The installed-browser channels Playwright can drive in place of its own build. */
19
+ export declare const ChromiumChannel: {
20
+ readonly CHROME: "chrome";
21
+ readonly MSEDGE: "msedge";
22
+ };
23
+ export type ChromiumChannel = (typeof ChromiumChannel)[keyof typeof ChromiumChannel];
24
+ export declare const CHANNEL_LABEL: Record<ChromiumChannel, string>;
25
+ /** Candidate executable paths for a channel on this platform; empty where it is not installable. */
26
+ export declare function channelExecutableCandidates(channel: ChromiumChannel, platform: NodeJS.Platform, env: NodeJS.ProcessEnv): string[];
27
+ /** The first installed Chrome/Edge on this machine, or undefined. Looks only; installs nothing. */
28
+ export declare function findInstalledChannel(deps: Pick<ChromiumTargetDeps, 'exists' | 'platform' | 'env'>): ChromiumChannel | undefined;
29
+ /** Everything the resolver touches, injected so it runs against a fake machine in tests. */
30
+ export interface ChromiumTargetDeps {
31
+ /** Where the bundled playwright expects its Chromium build. */
32
+ executablePath: () => string;
33
+ exists: (path: string) => boolean;
34
+ platform: NodeJS.Platform;
35
+ env: NodeJS.ProcessEnv;
36
+ /** Run the pinned Chromium install; resolves true when it succeeded. */
37
+ install: () => Promise<boolean>;
38
+ /** A progress line for a human — stderr in production. */
39
+ announce: (line: string) => void;
40
+ }
41
+ /** Which browser to launch: the bundled build (no channel), an installed channel, or none at all. */
42
+ export type ChromiumTarget = {
43
+ found: true;
44
+ channel?: ChromiumChannel;
45
+ } | {
46
+ found: false;
47
+ };
48
+ /**
49
+ * Decide which browser a launch will use, installing the pinned Chromium only as the last resort.
50
+ * Order: bundled build on disk → installed Chrome → installed Edge → install, then bundled.
51
+ */
52
+ export declare function resolveChromiumTarget(deps: ChromiumTargetDeps): Promise<ChromiumTarget>;
53
+ /** The subset of Playwright's `BrowserType` this file needs, so tests can pass a fake. */
54
+ export interface LaunchableChromium<B> {
55
+ executablePath: () => string;
56
+ launch: (options: ChromiumLaunchOptions) => Promise<B>;
57
+ }
58
+ /**
59
+ * A progress writer that says each distinct line once. The Chrome/Edge fallback line was written on
60
+ * every launch, and the pool launches per context, so one daemon repeated the same notice into the
61
+ * terminal of whoever started it.
62
+ */
63
+ export declare function announceOnce(write: (line: string) => void): (line: string) => void;
64
+ /**
65
+ * Launch a Chromium with the shared options, on whichever browser this machine can offer. When none
66
+ * can be found or installed, launch anyway: Playwright's own error is what `chromiumLaunchHint`
67
+ * turns into the pinned install command, so the user still gets the one line that fixes it.
68
+ */
69
+ export declare function launchChromium<B>(chromium: LaunchableChromium<B>, headless: boolean, deps?: ChromiumTargetDeps): Promise<B>;
70
+ /**
71
+ * The lease preflight's probe: "can a launch succeed?", which now includes the Chrome/Edge fallback
72
+ * and the automatic install. Without this the preflight would refuse a lease on a machine the
73
+ * launcher itself could serve. Falls back to the plain probe so a real refusal keeps its evidence.
74
+ */
75
+ export declare function probeLaunchableChromium(): Promise<ChromiumProbe>;
76
+ /**
77
+ * `doctor`'s probe: the plain Chromium probe, plus the installed browser that stands in when the
78
+ * bundled build is missing. Never installs — a diagnostic that downloads 150 MiB is not one.
79
+ */
80
+ export declare function probeChromiumWithFallback(): Promise<ChromiumProbe>;