@reticlehq/server 3.9.0 → 3.10.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 (350) hide show
  1. package/SKILL.md +13 -12
  2. package/dist/command/cli/adhoc-suite.js +2 -0
  3. package/dist/command/cli/adhoc-verdict.d.ts +2 -43
  4. package/dist/command/cli/adhoc-verdict.js +50 -187
  5. package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
  6. package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
  7. package/dist/command/cli/cli-doctor.js +1 -1
  8. package/dist/command/cli/cli-flow-commands.js +2 -9
  9. package/dist/command/cli/cli-kill.js +1 -1
  10. package/dist/command/cli/cli-parse-compare.d.ts +22 -0
  11. package/dist/command/cli/cli-parse-compare.js +75 -0
  12. package/dist/command/cli/cli-parse-grade.d.ts +36 -0
  13. package/dist/command/cli/cli-parse-grade.js +75 -0
  14. package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
  15. package/dist/command/cli/cli-parse-grammar.js +18 -20
  16. package/dist/command/cli/cli-parse-verify.d.ts +10 -0
  17. package/dist/command/cli/cli-parse-verify.js +63 -14
  18. package/dist/command/cli/cli-parse.d.ts +17 -15
  19. package/dist/command/cli/cli-parse.js +44 -27
  20. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  21. package/dist/command/cli/cli-update-commands.js +29 -20
  22. package/dist/command/cli/cli-usage.js +58 -29
  23. package/dist/command/cli/cli-verify.d.ts +11 -2
  24. package/dist/command/cli/cli-verify.js +27 -8
  25. package/dist/command/cli/cloud-cli.js +2 -119
  26. package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
  27. package/dist/command/cli/daemon-link/daemon-client.js +205 -0
  28. package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
  29. package/dist/command/cli/doctor-repairs.js +1 -1
  30. package/dist/command/cli/ensure-daemon.d.ts +26 -0
  31. package/dist/command/cli/ensure-daemon.js +57 -0
  32. package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
  33. package/dist/command/cli/finished-run/grade-command.js +180 -0
  34. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
  35. package/dist/command/cli/lifecycle/daemon-lifecycle.js +1 -1
  36. package/dist/command/cli/ports/port-holder.d.ts +9 -0
  37. package/dist/command/cli/ports/port-holder.js +27 -3
  38. package/dist/command/cli/status/status-lines.d.ts +1 -0
  39. package/dist/command/cli/status/status-lines.js +22 -0
  40. package/dist/command/cli/status-fields.d.ts +5 -1
  41. package/dist/command/cli/status-fields.js +7 -3
  42. package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
  43. package/dist/command/cli/two-builds/compare-command.js +314 -0
  44. package/dist/command/cli/verify-against.d.ts +61 -0
  45. package/dist/command/cli/verify-against.js +187 -0
  46. package/dist/command/cli/verify-no-session.d.ts +2 -0
  47. package/dist/command/cli/verify-no-session.js +13 -0
  48. package/dist/command/cli/verify-results.d.ts +35 -2
  49. package/dist/command/cli/verify-results.js +76 -10
  50. package/dist/command/cli.js +46 -41
  51. package/dist/command/daemon/daemon-resolve.d.ts +8 -1
  52. package/dist/command/daemon/daemon-resolve.js +27 -1
  53. package/dist/command/daemon/daemon.d.ts +12 -1
  54. package/dist/command/daemon/daemon.js +35 -4
  55. package/dist/command/daemon/lifetime/death-cause.js +21 -2
  56. package/dist/command/daemon/lifetime/heartbeat.js +7 -2
  57. package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
  58. package/dist/command/daemon/lifetime/startup-failure.js +62 -0
  59. package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
  60. package/dist/command/setup/bringup/dev-server-files.js +17 -0
  61. package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
  62. package/dist/command/setup/bringup/static-page-server.js +43 -9
  63. package/dist/command/setup/init/init-runtime.js +6 -0
  64. package/dist/command/setup/node-effects.d.ts +3 -2
  65. package/dist/command/setup/node-effects.js +9 -3
  66. package/dist/command/setup/remaining-steps.d.ts +8 -0
  67. package/dist/command/setup/remaining-steps.js +10 -0
  68. package/dist/command/setup/run-setup.d.ts +2 -0
  69. package/dist/command/setup/run-setup.js +9 -1
  70. package/dist/command/setup/setup-command.js +6 -3
  71. package/dist/command/update/updater.js +6 -0
  72. package/dist/command/version/version-skew.js +1 -1
  73. package/dist/features/compare/action-plan.d.ts +260 -0
  74. package/dist/features/compare/action-plan.js +543 -0
  75. package/dist/features/compare/behaviour-compare.d.ts +24 -0
  76. package/dist/features/compare/behaviour-compare.js +85 -0
  77. package/dist/features/compare/behaviour-diff.d.ts +97 -0
  78. package/dist/features/compare/behaviour-diff.js +202 -0
  79. package/dist/features/compare/behaviour-report.d.ts +40 -0
  80. package/dist/features/compare/behaviour-report.js +128 -0
  81. package/dist/features/compare/build-readings.d.ts +106 -0
  82. package/dist/features/compare/build-readings.js +250 -0
  83. package/dist/features/compare/compare-diff.d.ts +62 -0
  84. package/dist/features/compare/compare-diff.js +177 -0
  85. package/dist/features/compare/compare-report-html.d.ts +6 -0
  86. package/dist/features/compare/compare-report-html.js +102 -0
  87. package/dist/features/compare/compare-report.d.ts +15 -0
  88. package/dist/features/compare/compare-report.js +73 -0
  89. package/dist/features/compare/compare-run.d.ts +60 -0
  90. package/dist/features/compare/compare-run.js +123 -0
  91. package/dist/features/compare/outside/effect-lines.d.ts +26 -0
  92. package/dist/features/compare/outside/effect-lines.js +103 -0
  93. package/dist/features/compare/outside/state-sources.d.ts +76 -0
  94. package/dist/features/compare/outside/state-sources.js +164 -0
  95. package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
  96. package/dist/features/compare/outside/value-dictionary.js +227 -0
  97. package/dist/features/compare/visible-content.d.ts +20 -0
  98. package/dist/features/compare/visible-content.js +125 -0
  99. package/dist/features/crawl/crawl-empty.js +2 -1
  100. package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
  101. package/dist/features/crawl/crawl-exhaustive.js +138 -0
  102. package/dist/features/crawl/crawl-tools.d.ts +9 -2
  103. package/dist/features/crawl/crawl-tools.js +28 -12
  104. package/dist/features/crawl/crawl.d.ts +8 -9
  105. package/dist/features/crawl/crawl.js +61 -65
  106. package/dist/features/exhaust/app-coverage.d.ts +5 -1
  107. package/dist/features/exhaust/app-coverage.js +3 -1
  108. package/dist/features/exhaust/code-coverage.js +8 -4
  109. package/dist/features/exhaust/edge-cases.d.ts +133 -0
  110. package/dist/features/exhaust/edge-cases.js +310 -0
  111. package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
  112. package/dist/features/exhaust/edges/edge-drive.js +364 -0
  113. package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
  114. package/dist/features/exhaust/edges/edge-run.js +169 -0
  115. package/dist/features/exhaust/explorer.d.ts +87 -14
  116. package/dist/features/exhaust/explorer.js +159 -25
  117. package/dist/features/exhaust/failure-branches.d.ts +5 -0
  118. package/dist/features/exhaust/failure-branches.js +29 -11
  119. package/dist/features/exhaust/invariants.d.ts +112 -0
  120. package/dist/features/exhaust/invariants.js +188 -0
  121. package/dist/features/exhaust/ledger.d.ts +68 -0
  122. package/dist/features/exhaust/ledger.js +121 -13
  123. package/dist/features/exhaust/session-fold.d.ts +10 -0
  124. package/dist/features/exhaust/session-fold.js +46 -3
  125. package/dist/features/exhaust/session-port.d.ts +3 -1
  126. package/dist/features/exhaust/session-port.js +97 -28
  127. package/dist/features/grade/grade-answer.d.ts +99 -0
  128. package/dist/features/grade/grade-answer.js +112 -0
  129. package/dist/features/grade/grade-noise.d.ts +27 -0
  130. package/dist/features/grade/grade-noise.js +55 -0
  131. package/dist/features/grade/grade-renumber.d.ts +12 -0
  132. package/dist/features/grade/grade-renumber.js +86 -0
  133. package/dist/features/grade/grade-state.d.ts +50 -0
  134. package/dist/features/grade/grade-state.js +93 -0
  135. package/dist/features/grade/grade-suggest.d.ts +33 -0
  136. package/dist/features/grade/grade-suggest.js +98 -0
  137. package/dist/features/visual/visual-tools.js +66 -19
  138. package/dist/index.js +7 -4
  139. package/dist/judgement/capsule/causal-summary.js +9 -2
  140. package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
  141. package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
  142. package/dist/judgement/runs/reticle-runner.js +16 -2
  143. package/dist/judgement/runs/runner-port.d.ts +2 -0
  144. package/dist/judgement/runs/runner-port.js +23 -5
  145. package/dist/language/flows/affected-tools.d.ts +1 -1
  146. package/dist/language/flows/affected-tools.js +49 -30
  147. package/dist/language/flows/change/git-changed.d.ts +2 -1
  148. package/dist/language/flows/change/git-changed.js +19 -2
  149. package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
  150. package/dist/language/flows/fields/flow-secret-field.js +77 -21
  151. package/dist/language/flows/flow-anchor.d.ts +60 -2
  152. package/dist/language/flows/flow-anchor.js +125 -20
  153. package/dist/language/flows/flow-classify.d.ts +20 -0
  154. package/dist/language/flows/flow-classify.js +90 -12
  155. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  156. package/dist/language/flows/flow-cross-step.js +25 -1
  157. package/dist/language/flows/flow-journey.d.ts +38 -4
  158. package/dist/language/flows/flow-journey.js +138 -8
  159. package/dist/language/flows/flow-replay-run.d.ts +0 -16
  160. package/dist/language/flows/flow-replay-run.js +76 -14
  161. package/dist/language/flows/flow-replay-types.d.ts +4 -2
  162. package/dist/language/flows/flow-replay.d.ts +19 -1
  163. package/dist/language/flows/flow-replay.js +169 -52
  164. package/dist/language/flows/flow-replayable.js +7 -1
  165. package/dist/language/flows/flow-start-path.d.ts +34 -5
  166. package/dist/language/flows/flow-start-path.js +202 -15
  167. package/dist/language/flows/flow-step-runners.d.ts +14 -13
  168. package/dist/language/flows/flow-step-runners.js +184 -109
  169. package/dist/language/flows/flow-store-for-session.d.ts +26 -25
  170. package/dist/language/flows/flow-store-for-session.js +44 -0
  171. package/dist/language/flows/flow-tools.js +46 -33
  172. package/dist/language/flows/flows.js +176 -76
  173. package/dist/language/flows/recording/tape/recordings.d.ts +19 -3
  174. package/dist/language/flows/recording/tape/recordings.js +71 -5
  175. package/dist/language/flows/replay.d.ts +19 -5
  176. package/dist/language/flows/replay.js +99 -7
  177. package/dist/language/flows/steps/page-steps.d.ts +68 -0
  178. package/dist/language/flows/steps/page-steps.js +198 -0
  179. package/dist/language/flows/suite/sequential-run.d.ts +68 -0
  180. package/dist/language/flows/suite/sequential-run.js +96 -0
  181. package/dist/language/flows/verify-change-tools.js +26 -15
  182. package/dist/memory/cloud/background-sync.d.ts +13 -0
  183. package/dist/memory/cloud/background-sync.js +24 -3
  184. package/dist/memory/cloud/sync-daemon.js +26 -27
  185. package/dist/memory/journal/attach-journal.js +2 -1
  186. package/dist/memory/journal/drive-flow.d.ts +7 -0
  187. package/dist/memory/journal/drive-flow.js +44 -13
  188. package/dist/memory/journal/journal-recorder.d.ts +4 -2
  189. package/dist/memory/journal/session-journal.d.ts +7 -0
  190. package/dist/memory/journal/session-journal.js +58 -10
  191. package/dist/memory/project/root-claims.d.ts +9 -0
  192. package/dist/memory/project/root-claims.js +9 -4
  193. package/dist/portal/input/download-capture.d.ts +7 -0
  194. package/dist/portal/input/download-capture.js +92 -0
  195. package/dist/portal/input/network-detail.d.ts +6 -0
  196. package/dist/portal/input/network-detail.js +12 -1
  197. package/dist/portal/input/network-mock-tools.js +49 -13
  198. package/dist/portal/input/network-mock.d.ts +8 -0
  199. package/dist/portal/input/network-mock.js +28 -9
  200. package/dist/portal/input/page-capture.d.ts +24 -0
  201. package/dist/portal/input/page-capture.js +33 -0
  202. package/dist/portal/input/real-input.d.ts +22 -20
  203. package/dist/portal/input/real-input.js +41 -31
  204. package/dist/portal/input/viewport-tools.d.ts +8 -0
  205. package/dist/portal/input/viewport-tools.js +75 -18
  206. package/dist/portal/pool/browser-pool.d.ts +11 -29
  207. package/dist/portal/pool/browser-pool.js +35 -17
  208. package/dist/portal/pool/context-permissions.d.ts +17 -3
  209. package/dist/portal/pool/context-permissions.js +36 -5
  210. package/dist/portal/pool/determinism.d.ts +52 -0
  211. package/dist/portal/pool/determinism.js +259 -0
  212. package/dist/portal/pool/lease-emulation.d.ts +34 -1
  213. package/dist/portal/pool/lease-emulation.js +18 -0
  214. package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
  215. package/dist/portal/pool/lease-marker-stamp.js +15 -1
  216. package/dist/portal/pool/lease-page-error.d.ts +44 -17
  217. package/dist/portal/pool/lease-page-error.js +147 -5
  218. package/dist/portal/pool/page-error-journal.d.ts +7 -1
  219. package/dist/portal/pool/page-error-journal.js +27 -0
  220. package/dist/portal/pool/playwright-launcher.js +51 -8
  221. package/dist/portal/pool/pool-contract.d.ts +27 -1
  222. package/dist/portal/pool/pool-lease.d.ts +36 -1
  223. package/dist/portal/pool/zero-install.d.ts +8 -0
  224. package/dist/portal/pool/zero-install.js +23 -1
  225. package/dist/portal/session/dev-server/dev-command.d.ts +7 -6
  226. package/dist/portal/session/dev-server/dev-command.js +61 -15
  227. package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
  228. package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
  229. package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
  230. package/dist/portal/session/dev-server/project-ports.js +62 -0
  231. package/dist/portal/session/facts/departed-network.d.ts +66 -0
  232. package/dist/portal/session/facts/departed-network.js +142 -0
  233. package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
  234. package/dist/portal/session/no-session-diagnosis.js +67 -25
  235. package/dist/portal/session/no-session-next-action.d.ts +2 -0
  236. package/dist/portal/session/no-session-next-action.js +12 -3
  237. package/dist/portal/session/no-session-watch.d.ts +6 -0
  238. package/dist/portal/session/no-session-watch.js +38 -3
  239. package/dist/portal/session/serving-directory.d.ts +10 -0
  240. package/dist/portal/session/serving-directory.js +39 -8
  241. package/dist/portal/session/session-manager.d.ts +10 -0
  242. package/dist/portal/session/session-manager.js +174 -63
  243. package/dist/start-wiring.d.ts +2 -1
  244. package/dist/start-wiring.js +2 -1
  245. package/dist/surface/mcp/mcp.js +3 -2
  246. package/dist/surface/mcp/shared-params.d.ts +2 -0
  247. package/dist/surface/mcp/shared-params.js +31 -0
  248. package/dist/surface/tools/act/act-observation.d.ts +40 -2
  249. package/dist/surface/tools/act/act-observation.js +118 -12
  250. package/dist/surface/tools/act/act-preflight.js +1 -1
  251. package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
  252. package/dist/surface/tools/act/act-sequence-retry.js +61 -5
  253. package/dist/surface/tools/act/after-match.d.ts +4 -1
  254. package/dist/surface/tools/act/after-match.js +8 -1
  255. package/dist/surface/tools/act/already-true.js +1 -1
  256. package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
  257. package/dist/surface/tools/act/navigation/reload-result.js +25 -2
  258. package/dist/surface/tools/act/resolve-target.d.ts +15 -1
  259. package/dist/surface/tools/act/resolve-target.js +24 -1
  260. package/dist/surface/tools/act/sequence-grade.d.ts +12 -0
  261. package/dist/surface/tools/act/sequence-grade.js +37 -26
  262. package/dist/surface/tools/act/settle-in-flight.js +27 -15
  263. package/dist/surface/tools/act-command.d.ts +23 -0
  264. package/dist/surface/tools/act-command.js +50 -0
  265. package/dist/surface/tools/act-sequence-tool.js +78 -50
  266. package/dist/surface/tools/act-tools.d.ts +1 -13
  267. package/dist/surface/tools/act-tools.js +74 -86
  268. package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
  269. package/dist/surface/tools/agent/build-staleness.js +1 -1
  270. package/dist/surface/tools/assert/assert-verdict.js +16 -37
  271. package/dist/surface/tools/browser-tools.js +12 -6
  272. package/dist/surface/tools/compare-tools.d.ts +11 -0
  273. package/dist/surface/tools/compare-tools.js +94 -0
  274. package/dist/surface/tools/coverage-tools.js +1 -1
  275. package/dist/surface/tools/dynamic-tools.js +9 -2
  276. package/dist/surface/tools/error-recovery.d.ts +12 -1
  277. package/dist/surface/tools/error-recovery.js +91 -21
  278. package/dist/surface/tools/explore-tools.js +1 -1
  279. package/dist/surface/tools/invoke-tool.js +3 -2
  280. package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
  281. package/dist/surface/tools/lease-coverage-tools.js +128 -0
  282. package/dist/surface/tools/lease-hint.d.ts +35 -1
  283. package/dist/surface/tools/lease-hint.js +42 -0
  284. package/dist/surface/tools/lease-readiness.d.ts +26 -0
  285. package/dist/surface/tools/lease-readiness.js +79 -0
  286. package/dist/surface/tools/lease-seed.d.ts +14 -1
  287. package/dist/surface/tools/lease-seed.js +35 -3
  288. package/dist/surface/tools/lease-tools.d.ts +3 -41
  289. package/dist/surface/tools/lease-tools.js +62 -110
  290. package/dist/surface/tools/lease-url-identity.d.ts +46 -0
  291. package/dist/surface/tools/lease-url-identity.js +97 -0
  292. package/dist/surface/tools/merge-tools.js +99 -2
  293. package/dist/surface/tools/merged-name-redirect.js +5 -1
  294. package/dist/surface/tools/observe-tools.js +15 -9
  295. package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
  296. package/dist/surface/tools/read/console-pre-attach.js +23 -0
  297. package/dist/surface/tools/read-tools.js +33 -30
  298. package/dist/surface/tools/real-input-attempt.d.ts +0 -8
  299. package/dist/surface/tools/real-input-attempt.js +143 -28
  300. package/dist/surface/tools/session-ports.d.ts +14 -0
  301. package/dist/surface/tools/session-ports.js +53 -0
  302. package/dist/surface/tools/tool-kit.d.ts +6 -0
  303. package/dist/surface/tools/tool-kit.js +2 -3
  304. package/dist/surface/tools/tool-surface.js +0 -1
  305. package/dist/surface/tools/tools.js +28 -5
  306. package/dist/surface/tools/type-spelling.d.ts +13 -0
  307. package/dist/surface/tools/type-spelling.js +36 -0
  308. package/dist/telemetry/agent-client.d.ts +6 -0
  309. package/dist/telemetry/agent-client.js +10 -0
  310. package/dist/telemetry/argument-shape.js +1 -0
  311. package/dist/telemetry/tool-refused.d.ts +6 -3
  312. package/dist/telemetry/tool-refused.js +8 -3
  313. package/docs/actions.mdx +3 -3
  314. package/docs/agent-cheatsheet.md +1 -1
  315. package/docs/capabilities.mdx +61 -1
  316. package/docs/cli/affected.mdx +9 -7
  317. package/docs/cli/cloud.mdx +3 -3
  318. package/docs/cli/compare.mdx +94 -0
  319. package/docs/cli/doctor.mdx +1 -1
  320. package/docs/cli/grade.mdx +97 -0
  321. package/docs/cli/license.mdx +9 -7
  322. package/docs/cli/restart.mdx +7 -5
  323. package/docs/cli/rollback.mdx +5 -5
  324. package/docs/cli/stop.mdx +3 -3
  325. package/docs/cli/update.mdx +2 -2
  326. package/docs/cli/verify.mdx +23 -1
  327. package/docs/cli.mdx +33 -23
  328. package/docs/deploy-checks.md +1 -1
  329. package/docs/desktop.mdx +1 -1
  330. package/docs/enterprise.md +2 -2
  331. package/docs/environment.mdx +1 -1
  332. package/docs/flows.md +11 -1
  333. package/docs/frameworks.mdx +9 -9
  334. package/docs/getting-started.md +1 -1
  335. package/docs/install-manual.mdx +1 -1
  336. package/docs/license-activation.mdx +3 -3
  337. package/docs/packages/tauri.mdx +1 -1
  338. package/docs/packages.mdx +1 -1
  339. package/docs/predicates.mdx +1 -1
  340. package/docs/quickstart.mdx +68 -49
  341. package/docs/skill-setup.md +1 -1
  342. package/docs/skill.md +14 -13
  343. package/docs/telemetry.md +2 -2
  344. package/docs/tools/navigate.mdx +1 -1
  345. package/docs/tools/verify.mdx +4 -1
  346. package/docs/troubleshooting.mdx +1 -1
  347. package/docs/usage.md +17 -14
  348. package/package.json +13 -13
  349. package/dist/features/exhaust/exhaust-run.d.ts +0 -33
  350. package/dist/features/exhaust/exhaust-run.js +0 -82
@@ -5,7 +5,7 @@ import { safeProjectId } from './flow-result.js';
5
5
  import { changeInPlace } from './narrow-write.js';
6
6
  import { dirname } from 'node:path';
7
7
  import { gitTracks } from '../../machine/git-tracks.js';
8
- import { AnchorKind, DEGRADED_ANCHOR_ROLE, DOCUMENT_ANCHOR_ROLE, FLOW_FILE_VERSION, flowFileVersionFor, FlowErrorCode, FlowFileSchema, FlowStepTool, QueryBy, defaultIsSensitiveKey, isGlobalPressCall, } from '@reticlehq/core';
8
+ import { AnchorKind, DEGRADED_ANCHOR_ROLE, DOCUMENT_ANCHOR_ROLE, FLOW_FILE_VERSION, flowFileVersionFor, FlowErrorCode, FlowFileSchema, FlowStepTool, QueryBy, REDACTED_VALUE, isCredentialFieldName, isGlobalPressCall, } from '@reticlehq/core';
9
9
  import { ReticleTool, flowJsonReplacer, mainCheckoutTwin } from '@reticlehq/core';
10
10
  import { asRecord, asString } from '@reticlehq/core';
11
11
  import { applyHealChanges } from './heal.js';
@@ -35,25 +35,44 @@ function sourceArg(src) {
35
35
  const source = asRecord(src['source']);
36
36
  const file = source['file'];
37
37
  const line = source['line'];
38
- if (typeof file !== 'string' || 0 === file.length || typeof line !== 'number')
38
+ if (typeof file !== 'string' || 0 === file.length || typeof line !== 'number') {
39
39
  return undefined;
40
+ }
40
41
  const out = { file, line };
41
- if ('number' === typeof source['column'])
42
+ if ('number' === typeof source['column']) {
42
43
  out.column = source['column'];
44
+ }
43
45
  return out;
44
46
  }
45
47
  function componentAnchor(src) {
46
48
  const component = asString(src['component']);
47
49
  const source = sourceArg(src);
48
- if (component === undefined && source === undefined)
50
+ if (component === undefined && source === undefined) {
49
51
  return null;
52
+ }
50
53
  const anchor = {
51
54
  kind: AnchorKind.COMPONENT,
52
55
  };
53
- if (component !== undefined)
56
+ if (component !== undefined) {
54
57
  anchor.component = component;
55
- if (source !== undefined)
58
+ }
59
+ if (source !== undefined) {
56
60
  anchor.source = source;
61
+ }
62
+ const text = asString(src['text']);
63
+ if (text !== undefined && text.length > 0) {
64
+ anchor.text = text;
65
+ }
66
+ const role = asString(src['role']);
67
+ if (role !== undefined && role.length > 0) {
68
+ anchor.role = role;
69
+ }
70
+ const nth = src['nth'];
71
+ const of = src['of'];
72
+ if ('number' === typeof nth && 'number' === typeof of && 1 < of) {
73
+ anchor.nth = nth;
74
+ anchor.of = of;
75
+ }
57
76
  return anchor;
58
77
  }
59
78
  /** Pick the on-disk anchor for a normalized step: testid > component(auto) > degraded role. */
@@ -66,20 +85,19 @@ export function anchorForStep(args) {
66
85
  value,
67
86
  };
68
87
  const source = sourceArg(args);
69
- if (source !== undefined)
88
+ if (source !== undefined) {
70
89
  anchor.source = source;
90
+ }
71
91
  const of = args['of'];
72
- if ('number' === typeof of && 1 < of)
92
+ if ('number' === typeof of && 1 < of) {
73
93
  anchor.of = of;
94
+ // Its place among them, as a role anchor keeps one (#2135).
95
+ Object.assign(anchor, 'number' === typeof args['nth'] ? { nth: args['nth'] } : {});
96
+ }
74
97
  return { anchor, degraded: false };
75
98
  }
76
- // Role + NAME is a real anchor, not a degraded placeholder.
77
- //
78
- // The vocabulary has always described `{ kind:'role', role, name }` as an addressable anchor, and
79
- // nothing ever produced one — the only ROLE anchor written to disk was the degraded placeholder
80
- // that means "add a data-testid". So a step the recorder anchored by accessible name arrived here
81
- // and had that name thrown away, which is precisely the anchor that distinguishes one row's
82
- // control from another's when a shared JSX source location cannot.
99
+ // Role + NAME is a real anchor, not a degraded placeholder: the name separates one row's control
100
+ // from another's when a shared JSX source location cannot.
83
101
  // A document-key press compiled with no element (#1559): a real anchor, not a degraded one. It is
84
102
  // nameless on purpose, because it names no element; the press is what replay dispatches.
85
103
  if (by === QueryBy.ROLE &&
@@ -88,20 +106,30 @@ export function anchorForStep(args) {
88
106
  isGlobalPressCall({ action: args['action'], args: args['args'] })) {
89
107
  return { anchor: { kind: AnchorKind.ROLE, role: DOCUMENT_ANCHOR_ROLE }, degraded: false };
90
108
  }
91
- if (by === QueryBy.ROLE && value !== undefined) {
109
+ if (by === QueryBy.ROLE && value !== undefined && value !== DEGRADED_ANCHOR_ROLE) {
92
110
  const name = asString(args['name']);
111
+ // Provenance lives on the step, not the anchor; the position among namesakes is identity.
112
+ const nth = args['nth'];
113
+ const of = args['of'];
114
+ const among = 'number' === typeof nth && 'number' === typeof of ? { nth, of } : {};
93
115
  if (name !== undefined && name.length > 0) {
94
- // Provenance lives on the step, not the anchor; the position among namesakes is identity.
95
- const nth = args['nth'];
96
- const of = args['of'];
97
- const among = 'number' === typeof nth && 'number' === typeof of ? { nth, of } : {};
98
- return { anchor: { kind: AnchorKind.ROLE, role: value, name, ...among }, degraded: false };
116
+ // The nearest containers' roles: where a replay looks when a correct build renamed it.
117
+ const within = Array.isArray(args['within']) ? { within: args['within'].map(String) } : {};
118
+ return {
119
+ anchor: { kind: AnchorKind.ROLE, role: value, name, ...among, ...within },
120
+ degraded: false,
121
+ };
99
122
  }
123
+ // A control with a role and no accessible name (an icon-only button) is addressed by its
124
+ // position among the nameless controls of its role. Saving it as the degraded placeholder made
125
+ // every step on an icon-only button unreplayable.
126
+ return { anchor: { kind: AnchorKind.ROLE, role: value, ...among }, degraded: false };
100
127
  }
101
128
  if (by === QueryBy.COMPONENT) {
102
129
  const anchor = componentAnchor(args);
103
- if (anchor !== null)
130
+ if (anchor !== null) {
104
131
  return { anchor, degraded: false };
132
+ }
105
133
  }
106
134
  return { anchor: degradedAnchor(), degraded: true };
107
135
  }
@@ -122,8 +150,9 @@ function subStepToFlowStep(raw) {
122
150
  * exactly this, so dropping it here made the grader count assertions that were never saved.
123
151
  */
124
152
  const expect = sub['expect'];
125
- if (expect !== undefined)
153
+ if (expect !== undefined) {
126
154
  step.expect = expect;
155
+ }
127
156
  return step;
128
157
  }
129
158
  /**
@@ -137,24 +166,55 @@ function subStepToFlowStep(raw) {
137
166
  * WHERE it was typed rather than by guessing whether the characters look secret — a guess that both
138
167
  * over-redacts a search box and misses a password that happens to be a dictionary word.
139
168
  *
140
- * `defaultIsSensitiveKey` is the same rule the network channel redacts by, so a field considered
141
- * sensitive on the wire cannot be considered safe on disk.
169
+ * `isCredentialFieldName` is the network key rule plus the word for a password in other
170
+ * languages. An SDK before 3.8.0 sends no secret mark, and then the name is the only signal.
142
171
  */
143
172
  function redactSecretFill(anchor, args,
144
173
  /** The SDK said the target holds a secret: redact whatever the field is called. */
145
174
  secret = false) {
146
- if (typeof args['value'] !== 'string')
175
+ if (typeof args['value'] !== 'string') {
147
176
  return args;
177
+ }
148
178
  const field = anchorFieldName(anchor);
149
- const sensitive = secret || (field !== undefined && defaultIsSensitiveKey(field));
150
- if (!sensitive)
179
+ const sensitive = secret || (field !== undefined && isCredentialFieldName(field));
180
+ if (!sensitive) {
151
181
  return args;
182
+ }
152
183
  // ponytail: a secret field with no name (a component anchor) gets no variable; replay then refuses it.
153
- if (field === undefined)
184
+ if (field === undefined) {
154
185
  return { ...args, value: REDACTED_FILL };
186
+ }
155
187
  // The variable that supplies it, beside the placeholder: the file alone says what to set.
156
188
  return { ...args, value: REDACTED_FILL, [SECRET_NAME_ARG]: secretEnvKey(field) };
157
189
  }
190
+ /**
191
+ * An anchored flow with each credential fill redacted by its field name.
192
+ *
193
+ * An in-page recorder of an older SDK redacts by an English rule only, and its flow arrives here
194
+ * already anchored. A value that is already a placeholder is left as it is, with its variable name.
195
+ */
196
+ function redactSavedFills(flow) {
197
+ let redacted = false;
198
+ const redact = (step) => {
199
+ const value = step.args?.['value'];
200
+ let out = step;
201
+ if (step.args !== undefined &&
202
+ 'string' === typeof value &&
203
+ REDACTED_FILL !== value &&
204
+ REDACTED_VALUE !== value) {
205
+ const args = redactSecretFill(step.anchor, step.args);
206
+ if (args !== step.args) {
207
+ redacted = true;
208
+ out = { ...step, args };
209
+ }
210
+ }
211
+ return step.steps === undefined ? out : { ...out, steps: step.steps.map(redact) };
212
+ };
213
+ const steps = flow.steps.map(redact);
214
+ // Only a fill redacted here needs a variable name. A flow that already names its variables keeps
215
+ // them: two fills that share one older name must not be renamed.
216
+ return redacted ? withDistinctSecretKeys({ ...flow, steps }) : flow;
217
+ }
158
218
  /** Whether a recorded step's args carry the SDK's secret mark. */
159
219
  const markedSecret = (args) => true === args[SECRET_FIELD_ARG];
160
220
  /**
@@ -176,8 +236,9 @@ export function redactRecordedSteps(steps) {
176
236
  /** One normalized act's args with its inner fill `args` redacted by the anchor it would save as. */
177
237
  function redactNormalizedArgs(normalized) {
178
238
  const inner = normalized['args'];
179
- if ('object' !== typeof inner || null === inner)
239
+ if ('object' !== typeof inner || null === inner) {
180
240
  return normalized;
241
+ }
181
242
  const redacted = redactSecretFill(anchorForStep(normalized).anchor, asRecord(inner), markedSecret(normalized));
182
243
  return redacted === inner ? normalized : { ...normalized, args: redacted };
183
244
  }
@@ -195,12 +256,15 @@ function redactNormalizedArgs(normalized) {
195
256
  * the placeholder into the form. One function decides it for both sides, or the two drift again.
196
257
  */
197
258
  export function anchorFieldName(anchor) {
198
- if (AnchorKind.TESTID === anchor.kind)
259
+ if (AnchorKind.TESTID === anchor.kind) {
199
260
  return anchor.value;
200
- if (AnchorKind.ROLE === anchor.kind)
261
+ }
262
+ if (AnchorKind.ROLE === anchor.kind) {
201
263
  return anchor.name;
202
- if (AnchorKind.SIGNAL === anchor.kind)
264
+ }
265
+ if (AnchorKind.SIGNAL === anchor.kind) {
203
266
  return anchor.name;
267
+ }
204
268
  return undefined;
205
269
  }
206
270
  /**
@@ -216,10 +280,12 @@ function keepRoleSource(step, src) {
216
280
  }
217
281
  function buildStep(tool, anchor, action, args, degraded, secret = false) {
218
282
  const step = { tool, anchor, args: redactSecretFill(anchor, args, secret) };
219
- if (action !== undefined)
283
+ if (action !== undefined) {
220
284
  step.action = action;
221
- if (degraded)
285
+ }
286
+ if (degraded) {
222
287
  step.degraded = true;
288
+ }
223
289
  return step;
224
290
  }
225
291
  /**
@@ -229,44 +295,51 @@ function buildStep(tool, anchor, action, args, degraded, secret = false) {
229
295
  */
230
296
  export function recordedStepToFlowStep(step) {
231
297
  const out = anchoredStep(step);
232
- if (step.timeoutMs !== undefined)
298
+ if (step.timeoutMs !== undefined) {
233
299
  out.timeoutMs = step.timeoutMs;
234
- if (step.page !== undefined)
300
+ }
301
+ if (step.burstMs !== undefined) {
302
+ out.burstMs = step.burstMs;
303
+ }
304
+ if (step.page !== undefined) {
235
305
  out.page = step.page;
236
- if (step.endPage !== undefined && step.endPage !== step.page)
306
+ }
307
+ if (step.endPage !== undefined && step.endPage !== step.page) {
237
308
  out.endPage = step.endPage;
238
- if (true === step.writes)
309
+ }
310
+ if (true === step.writes) {
239
311
  out.writes = true;
312
+ }
240
313
  return out;
241
314
  }
242
315
  function anchoredStep(step) {
243
316
  if (step.invoke !== undefined) {
244
317
  // An invocation drives nothing: no action, no args, and an anchor only because every step
245
- // carries one. Falling through to the action path below would give it an `action` and a
246
- // replayer would try to dispatch a step whose whole meaning is "go and run that document".
318
+ // carries one. Below, it would get an `action` that a replayer would try to dispatch.
247
319
  const out = {
248
320
  tool: FlowStepTool.INVOKE,
249
321
  anchor: { kind: AnchorKind.TESTID, value: step.invoke },
250
322
  invoke: step.invoke,
251
323
  };
252
- if (step.expect !== undefined)
324
+ if (step.expect !== undefined) {
253
325
  out.expect = step.expect;
326
+ }
254
327
  return out;
255
328
  }
256
329
  if (step.tool === ReticleTool.ACT_SEQUENCE) {
257
330
  const rawSubs = Array.isArray(step.args['steps']) ? step.args['steps'] : [];
258
331
  const subs = rawSubs.map(subStepToFlowStep);
259
332
  const degraded = subs.some((s) => true === s.degraded);
260
- // The first sub-step that HAS an anchor, not blindly the first. Taking subs[0] handed the whole
261
- // sequence the degraded sentinel whenever sub-step 0 lacked a testid — even with every later
262
- // sub-step perfectly anchored — and replay then queried the DOM for a testid literally named
263
- // "unresolved", so the step drifted on all 5 apps, every replay.
333
+ // The first sub-step that HAS an anchor: subs[0] gave a sequence the degraded sentinel whenever
334
+ // sub-step 0 lacked a testid, and replay looked for a testid named "unresolved" on every app.
264
335
  const anchor = subs.find((s) => s.degraded !== true)?.anchor ?? subs[0]?.anchor ?? degradedAnchor();
265
336
  const out = { tool: ReticleTool.ACT_SEQUENCE, anchor, steps: subs };
266
- if (degraded)
337
+ if (degraded) {
267
338
  out.degraded = true;
268
- if (step.expect !== undefined)
339
+ }
340
+ if (step.expect !== undefined) {
269
341
  out.expect = step.expect;
342
+ }
270
343
  return out;
271
344
  }
272
345
  const action = asString(step.args['action']);
@@ -274,14 +347,16 @@ function anchoredStep(step) {
274
347
  const { anchor, degraded } = anchorForStep(step.args);
275
348
  const out = buildStep(step.tool, anchor, action, args, degraded, markedSecret(step.args));
276
349
  keepRoleSource(out, step.args);
277
- if (step.expect !== undefined)
350
+ if (step.expect !== undefined) {
278
351
  out.expect = step.expect;
352
+ }
279
353
  return out;
280
354
  }
281
355
  /** Apply folded annotations onto an anchored flow (pure): per-step expect, dynamic[], success, intent. */
282
356
  function withAnnotations(flow, ann) {
283
- if (ann === undefined)
357
+ if (ann === undefined) {
284
358
  return flow;
359
+ }
285
360
  const steps = flow.steps.map((step, i) => {
286
361
  const expect = ann.stepExpect.get(i);
287
362
  return expect === undefined ? step : { ...step, expect };
@@ -290,10 +365,12 @@ function withAnnotations(flow, ann) {
290
365
  if (ann.dynamic.length > 0) {
291
366
  out.dynamic = ann.dynamic.map((value) => ({ kind: AnchorKind.TESTID, value }));
292
367
  }
293
- if (ann.success !== undefined)
368
+ if (ann.success !== undefined) {
294
369
  out.success = ann.success;
295
- if (ann.intent !== undefined)
370
+ }
371
+ if (ann.intent !== undefined) {
296
372
  out.intent = ann.intent;
373
+ }
297
374
  return out;
298
375
  }
299
376
  const JSON_INDENT = 2;
@@ -429,18 +506,21 @@ export class FlowStore {
429
506
  * fill its gaps and are removed. The same scan collapses copies saved before this existed.
430
507
  */
431
508
  async saveJourney(program, projectId, prefix) {
432
- if (!isValidFlowName(program.name))
509
+ if (!isValidFlowName(program.name)) {
433
510
  return { ok: false, code: FlowErrorCode.INVALID_NAME };
511
+ }
434
512
  const pid = safeProjectId(projectId);
435
513
  const fresh = this.#build(program, undefined, pid);
436
514
  const saved = [];
437
515
  const freshPrint = journeyFingerprint(fresh);
438
516
  for (const name of await this.list(pid)) {
439
517
  const loaded = await this.load(name, pid);
440
- if (!loaded.ok)
518
+ if (!loaded.ok) {
441
519
  continue;
442
- if (name.startsWith(prefix))
520
+ }
521
+ if (name.startsWith(prefix)) {
443
522
  saved.push(loaded.value);
523
+ }
444
524
  // The agent already saved this journey under its own name: one journey, one flow. A web drive
445
525
  // saved its named flow and an automatic `drive-saved-<email>` beside it, every time.
446
526
  else if (journeyFingerprint(loaded.value) === freshPrint) {
@@ -456,11 +536,13 @@ export class FlowStore {
456
536
  const taken = new Map(saved.map((flow) => [flow.name, journeyFingerprint(flow)]));
457
537
  // The same name holding this journey driven with a step more or less: one file, the fuller drive.
458
538
  const same = saved.find((flow) => flow.name === fresh.name && taken.get(flow.name) !== print);
459
- if (same !== undefined && withinJourney(same, fresh))
539
+ if (same !== undefined && withinJourney(same, fresh)) {
460
540
  return { ok: true, value: this.#summary(same) };
541
+ }
461
542
  const extended = same !== undefined && withinJourney(fresh, same);
462
- if (extended)
543
+ if (extended) {
463
544
  taken.delete(fresh.name);
545
+ }
464
546
  const named = {
465
547
  ...fresh,
466
548
  ...(extended && fresh.intent === undefined && same.intent !== undefined
@@ -471,19 +553,23 @@ export class FlowStore {
471
553
  byPrint.set(print, [...(byPrint.get(print) ?? []), named]);
472
554
  let result = named;
473
555
  for (const [groupPrint, group] of byPrint) {
474
- if (group.length < 2 && groupPrint !== print)
556
+ if (group.length < 2 && groupPrint !== print) {
475
557
  continue;
558
+ }
476
559
  const [kept, ...rest] = [...group].sort((a, b) => a.createdAt - b.createdAt);
477
- if (kept === undefined)
560
+ if (kept === undefined) {
478
561
  continue;
562
+ }
479
563
  const merged = await this.#linkIntent(rest.reduce(mergeJourney, kept));
480
564
  await this.#writeInPlace(merged, pid);
481
565
  for (const dropped of rest) {
482
- if (dropped.name !== merged.name && dropped !== named)
566
+ if (dropped.name !== merged.name && dropped !== named) {
483
567
  await this.remove(dropped.name, pid);
568
+ }
484
569
  }
485
- if (groupPrint === print)
570
+ if (groupPrint === print) {
486
571
  result = merged;
572
+ }
487
573
  }
488
574
  this.#onWrote?.();
489
575
  return { ok: true, value: this.#summary(result) };
@@ -501,8 +587,9 @@ export class FlowStore {
501
587
  * re-run FlowFileSchema before writing. save is left untouched.
502
588
  */
503
589
  async saveFlow(flow, projectId) {
504
- if (!isValidFlowName(flow.name))
590
+ if (!isValidFlowName(flow.name)) {
505
591
  return { ok: false, code: FlowErrorCode.INVALID_NAME };
592
+ }
506
593
  const pid = safeProjectId(projectId);
507
594
  // Stamp the project INTO the file (so a flow carries its own scope) and route it to the matching
508
595
  // per-project subdir. Both come from the same `pid`, so on-disk location and content always agree.
@@ -517,7 +604,7 @@ export class FlowStore {
517
604
  detail: describeFlowZodFailure(parsed.error),
518
605
  };
519
606
  }
520
- const valid = await this.#linkIntent(readyForReplay(parsed.data));
607
+ const valid = await this.#linkIntent(readyForReplay(redactSavedFills(parsed.data)));
521
608
  await this.#fs.mkdir(flowParentDir(this.#root, asFlowName(valid.name), pid));
522
609
  await this.#fs.writeFile(flowPath(this.#root, asFlowName(valid.name), pid), this.#serialize(valid));
523
610
  this.#onWrote?.();
@@ -578,8 +665,9 @@ export class FlowStore {
578
665
  }
579
666
  /** The `.json` basenames (no extension) directly inside `dir`. [] if the dir is absent/unreadable. */
580
667
  async #namesIn(dir) {
581
- if (!(await this.#fs.exists(dir)))
668
+ if (!(await this.#fs.exists(dir))) {
582
669
  return [];
670
+ }
583
671
  let entries;
584
672
  try {
585
673
  entries = await this.#fs.readdir(dir);
@@ -603,8 +691,9 @@ export class FlowStore {
603
691
  */
604
692
  async list(projectId) {
605
693
  const own = await this.#listOwn(projectId);
606
- if (this.#main === undefined)
694
+ if (this.#main === undefined) {
607
695
  return own;
696
+ }
608
697
  return [...new Set([...own, ...(await this.#main.list(projectId))])].sort();
609
698
  }
610
699
  async #listOwn(projectId) {
@@ -615,8 +704,9 @@ export class FlowStore {
615
704
  const own = await this.#namesIn(flowDir(this.#root, pid));
616
705
  return [...new Set([...own, ...legacy])].sort();
617
706
  }
618
- if (!(await this.#fs.exists(flowsDir)))
707
+ if (!(await this.#fs.exists(flowsDir))) {
619
708
  return [];
709
+ }
620
710
  let entries;
621
711
  try {
622
712
  entries = await this.#fs.readdir(flowsDir);
@@ -635,24 +725,28 @@ export class FlowStore {
635
725
  async #resolveReadPath(name, pid) {
636
726
  if (pid !== undefined) {
637
727
  const nested = flowPath(this.#root, name, pid);
638
- if (await this.#fs.exists(nested))
728
+ if (await this.#fs.exists(nested)) {
639
729
  return nested;
730
+ }
640
731
  }
641
732
  const flat = flowPath(this.#root, name);
642
- if (await this.#fs.exists(flat))
733
+ if (await this.#fs.exists(flat)) {
643
734
  return flat;
735
+ }
644
736
  // No projectId (CLI/CI/contract callers, e.g. reticle_domain): mirror list's subdir union on
645
737
  // the read side too — a flow saved under .reticle/flows/<projectId>/ must still load, else it is
646
738
  // listed and then silently dropped by `if (loaded.ok)` (reticle_domain reports flowCount:0).
647
- if (pid === undefined)
739
+ if (pid === undefined) {
648
740
  return this.#resolveNestedPath(name);
741
+ }
649
742
  return null;
650
743
  }
651
744
  /** Scan the per-project subdirs for a flow by name — the read-side of list's no-pid union. */
652
745
  async #resolveNestedPath(name) {
653
746
  const flowsDir = reticleDirPaths(this.#root).flows;
654
- if (!(await this.#fs.exists(flowsDir)))
747
+ if (!(await this.#fs.exists(flowsDir))) {
655
748
  return null;
749
+ }
656
750
  let entries;
657
751
  try {
658
752
  entries = await this.#fs.readdir(flowsDir);
@@ -663,8 +757,9 @@ export class FlowStore {
663
757
  // Same provenance as `#namesIn`'s scan above: a subdir of `.reticle/flows/` is a projectId.
664
758
  for (const dir of entries.filter((e) => !e.endsWith(FLOW_SUFFIX))) {
665
759
  const nested = flowPath(this.#root, name, asProjectId(dir));
666
- if (await this.#fs.exists(nested))
760
+ if (await this.#fs.exists(nested)) {
667
761
  return nested;
762
+ }
668
763
  }
669
764
  return null;
670
765
  }
@@ -674,16 +769,19 @@ export class FlowStore {
674
769
  */
675
770
  async load(name, projectId) {
676
771
  const own = await this.#loadOwn(name, projectId);
677
- if (own.ok || own.code !== FlowErrorCode.NOT_FOUND || this.#main === undefined)
772
+ if (own.ok || own.code !== FlowErrorCode.NOT_FOUND || this.#main === undefined) {
678
773
  return own;
774
+ }
679
775
  return this.#main.load(name, projectId);
680
776
  }
681
777
  async #loadOwn(name, projectId) {
682
- if (!isValidFlowName(name))
778
+ if (!isValidFlowName(name)) {
683
779
  return { ok: false, code: FlowErrorCode.INVALID_NAME };
780
+ }
684
781
  const path = await this.#resolveReadPath(name, safeProjectId(projectId));
685
- if (null === path)
782
+ if (null === path) {
686
783
  return { ok: false, code: FlowErrorCode.NOT_FOUND };
784
+ }
687
785
  let text;
688
786
  try {
689
787
  text = await this.#fs.readFile(path);
@@ -703,11 +801,13 @@ export class FlowStore {
703
801
  * not a silent no-op, so a typo doesn't read as success.
704
802
  */
705
803
  async remove(name, projectId) {
706
- if (!isValidFlowName(name))
804
+ if (!isValidFlowName(name)) {
707
805
  return { ok: false, code: FlowErrorCode.INVALID_NAME };
806
+ }
708
807
  const path = await this.#resolveReadPath(name, safeProjectId(projectId));
709
- if (null === path)
808
+ if (null === path) {
710
809
  return { ok: false, code: FlowErrorCode.NOT_FOUND };
810
+ }
711
811
  await this.#fs.rm(path);
712
812
  return { ok: true, value: undefined };
713
813
  }
@@ -9,6 +9,8 @@ export interface RecordedStep {
9
9
  stable: boolean;
10
10
  /** The caller's per-step wait budget, when one was explicitly provided. */
11
11
  timeoutMs?: number;
12
+ /** The act_sequence `burst` gap, when the sequence was driven with one. See FlowStep.burstMs. */
13
+ burstMs?: number;
12
14
  /** Optional post-condition annotation carried into the on-disk flow's expect. */
13
15
  expect?: Predicate;
14
16
  /**
@@ -130,8 +132,12 @@ export declare const isAmbient: (name: string) => boolean;
130
132
  export declare class RecordingStore {
131
133
  #private;
132
134
  private readonly byHarness;
133
- /** `byHarness`: whether the step being captured was driven by the Harness, which tapes its own. */
134
- constructor(byHarness?: () => boolean);
135
+ private readonly successorOf;
136
+ /**
137
+ * `byHarness`: whether the step being captured was driven by the Harness, which tapes its own.
138
+ * `successorOf`: the session that the same tab connected as after a page load, if any.
139
+ */
140
+ constructor(byHarness?: () => boolean, successorOf?: (session: string) => string | undefined);
135
141
  start(name: string, cursor: number, startPath?: string, session?: string, document?: string): void;
136
142
  isRecording(name: string): boolean;
137
143
  /**
@@ -201,7 +207,17 @@ export declare class RecordingStore {
201
207
  saveCompiled(program: CompiledProgram): void;
202
208
  getCompiled(name: string): CompiledProgram | undefined;
203
209
  active(): string[];
204
- /** The ambient tape of one tab (or of steps no tab named), taken: what its session end saves. */
210
+ /**
211
+ * The ambient tape of one tab (or of steps no tab named), taken: what its session end saves.
212
+ *
213
+ * A session also ends when the app itself loads a page (a form post on a server-rendered app), and
214
+ * the journey goes on in the page that loaded. A tape with no check is not saved, so the next
215
+ * page's tape starts with a copy of it: a fill and the submit that proves it are then one flow, not
216
+ * a fill that is dropped and a submit on an empty form. When Reticle navigated away, the agent
217
+ * started a new journey, and nothing is carried.
218
+ * ponytail: only a tape with NO check is carried; unchecked steps after a check stay behind.
219
+ * ponytail: kept for the last CARRY_CAP ended pages; a tab that never loads a successor drops them.
220
+ */
205
221
  takeAmbient(session: string | undefined): ActiveRecording | undefined;
206
222
  /**
207
223
  * Names of recordings that have been STOPPED and compiled.