@reticlehq/server 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (614) hide show
  1. package/SKILL.md +122 -123
  2. package/dist/command/cli/adhoc-suite.d.ts +14 -0
  3. package/dist/command/cli/adhoc-suite.js +42 -7
  4. package/dist/command/cli/adhoc-verdict.d.ts +18 -2
  5. package/dist/command/cli/adhoc-verdict.js +70 -6
  6. package/dist/command/cli/answers/gate-exit.d.ts +23 -0
  7. package/dist/command/cli/answers/gate-exit.js +45 -0
  8. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  9. package/dist/command/cli/answers/gate-hook-message.js +3 -1
  10. package/dist/command/cli/auth/device-flow.d.ts +13 -2
  11. package/dist/command/cli/auth/device-flow.js +17 -0
  12. package/dist/command/cli/cli-doctor.js +14 -0
  13. package/dist/command/cli/cli-flow-commands.d.ts +0 -11
  14. package/dist/command/cli/cli-flow-commands.js +28 -105
  15. package/dist/command/cli/cli-kill.js +3 -2
  16. package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
  17. package/dist/command/cli/cli-parse-grammar.js +75 -0
  18. package/dist/command/cli/cli-parse-verify.d.ts +2 -0
  19. package/dist/command/cli/cli-parse-verify.js +18 -0
  20. package/dist/command/cli/cli-parse.d.ts +19 -28
  21. package/dist/command/cli/cli-parse.js +82 -182
  22. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  23. package/dist/command/cli/cli-update-commands.js +61 -9
  24. package/dist/command/cli/cli-usage.d.ts +33 -0
  25. package/dist/command/cli/cli-usage.js +287 -0
  26. package/dist/command/cli/cli-verify.d.ts +34 -4
  27. package/dist/command/cli/cli-verify.js +81 -12
  28. package/dist/command/cli/cloud-cli.d.ts +13 -0
  29. package/dist/command/cli/cloud-cli.js +145 -333
  30. package/dist/command/cli/cloud-kit.d.ts +12 -0
  31. package/dist/command/cli/cloud-kit.js +41 -2
  32. package/dist/command/cli/cloud-link.d.ts +57 -0
  33. package/dist/command/cli/cloud-link.js +308 -0
  34. package/dist/command/cli/cloud-login.d.ts +15 -1
  35. package/dist/command/cli/cloud-login.js +67 -13
  36. package/dist/command/cli/context-command.d.ts +1 -0
  37. package/dist/command/cli/context-command.js +33 -0
  38. package/dist/command/cli/daemon-start-options.d.ts +3 -0
  39. package/dist/command/cli/daemon-start-options.js +24 -3
  40. package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
  41. package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
  42. package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
  43. package/dist/command/cli/doctor/doctor-project-line.js +5 -4
  44. package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
  45. package/dist/command/cli/doctor/doctor-rows.js +6 -0
  46. package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
  47. package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
  48. package/dist/command/cli/known-regression.d.ts +13 -0
  49. package/dist/command/cli/known-regression.js +34 -0
  50. package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
  51. package/dist/command/cli/launch/sdk-version-match.js +25 -0
  52. package/dist/command/cli/lifecycle/daemon-lifecycle.js +11 -2
  53. package/dist/command/cli/mcp-command.d.ts +6 -0
  54. package/dist/command/cli/mcp-command.js +13 -5
  55. package/dist/command/cli/ports/port-holder.js +1 -1
  56. package/dist/command/cli/project-name.d.ts +29 -1
  57. package/dist/command/cli/project-name.js +107 -2
  58. package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
  59. package/dist/command/cli/setup-mcp-cli.js +29 -8
  60. package/dist/command/cli/status/status-lines.d.ts +1 -0
  61. package/dist/command/cli/status/status-lines.js +47 -2
  62. package/dist/command/cli/status/workspace-links.d.ts +10 -0
  63. package/dist/command/cli/status/workspace-links.js +29 -0
  64. package/dist/command/cli/try-command.d.ts +96 -0
  65. package/dist/command/cli/try-command.js +362 -0
  66. package/dist/command/cli/tutorial.d.ts +0 -100
  67. package/dist/command/cli/tutorial.js +0 -178
  68. package/dist/command/cli/verify-results.d.ts +19 -0
  69. package/dist/command/cli/verify-results.js +29 -0
  70. package/dist/command/cli-onboarding.d.ts +0 -11
  71. package/dist/command/cli-onboarding.js +0 -15
  72. package/dist/command/cli.d.ts +14 -0
  73. package/dist/command/cli.js +103 -83
  74. package/dist/command/daemon/daemon-resilience.js +4 -4
  75. package/dist/command/daemon/daemon-resolve.d.ts +9 -1
  76. package/dist/command/daemon/daemon-resolve.js +49 -10
  77. package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
  78. package/dist/command/setup/bringup/bridge-port.js +13 -3
  79. package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
  80. package/dist/command/setup/bringup/ensure-daemon.js +9 -2
  81. package/dist/command/setup/init/agent-restart.d.ts +23 -0
  82. package/dist/command/setup/init/agent-restart.js +28 -0
  83. package/dist/command/setup/init/init-port.d.ts +10 -1
  84. package/dist/command/setup/init/init-port.js +22 -2
  85. package/dist/command/setup/init/init-runtime.d.ts +12 -2
  86. package/dist/command/setup/init/init-runtime.js +96 -37
  87. package/dist/command/setup/node-effects.d.ts +2 -0
  88. package/dist/command/setup/node-effects.js +36 -8
  89. package/dist/command/setup/probe/existing-app.d.ts +12 -0
  90. package/dist/command/setup/probe/existing-app.js +14 -0
  91. package/dist/command/setup/remaining-steps.js +1 -1
  92. package/dist/command/setup/run-setup.d.ts +29 -9
  93. package/dist/command/setup/run-setup.js +34 -28
  94. package/dist/command/setup/setup-command.d.ts +23 -0
  95. package/dist/command/setup/setup-command.js +57 -4
  96. package/dist/command/setup/setup-mcp.js +25 -2
  97. package/dist/command/setup/tauri-signing.d.ts +19 -0
  98. package/dist/command/setup/tauri-signing.js +79 -0
  99. package/dist/command/setup/terminal/listeners.d.ts +10 -1
  100. package/dist/command/setup/terminal/listeners.js +37 -6
  101. package/dist/command/update/update-nudge.d.ts +9 -0
  102. package/dist/command/update/update-nudge.js +1 -1
  103. package/dist/command/update/updater.js +11 -15
  104. package/dist/command/version/version-nudge.js +3 -0
  105. package/dist/command/version/version-skew.d.ts +20 -1
  106. package/dist/command/version/version-skew.js +38 -2
  107. package/dist/features/crawl/crawl.js +14 -1
  108. package/dist/features/exhaust/ledger.d.ts +14 -14
  109. package/dist/features/harness/drive-report.d.ts +39 -5
  110. package/dist/features/harness/drive-report.js +271 -54
  111. package/dist/features/harness/drive-runs.d.ts +120 -0
  112. package/dist/features/harness/drive-runs.js +289 -0
  113. package/dist/features/harness/drivers.d.ts +1 -1
  114. package/dist/features/harness/drivers.js +1 -1
  115. package/dist/features/harness/explore/console-pages.d.ts +15 -0
  116. package/dist/features/harness/explore/console-pages.js +146 -0
  117. package/dist/features/harness/explore/disparity.d.ts +19 -0
  118. package/dist/features/harness/explore/disparity.js +71 -0
  119. package/dist/features/harness/explore/fake-app.d.ts +16 -0
  120. package/dist/features/harness/explore/fake-app.js +49 -0
  121. package/dist/features/harness/explore/goal-search.d.ts +26 -0
  122. package/dist/features/harness/explore/goal-search.js +144 -0
  123. package/dist/features/harness/explore/page-tree.d.ts +49 -0
  124. package/dist/features/harness/explore/page-tree.js +148 -0
  125. package/dist/features/harness/explore/site-map.d.ts +34 -0
  126. package/dist/features/harness/explore/site-map.js +150 -0
  127. package/dist/features/harness/goals.d.ts +68 -7
  128. package/dist/features/harness/goals.js +289 -36
  129. package/dist/features/harness/harness.d.ts +30 -1
  130. package/dist/features/harness/harness.js +141 -7
  131. package/dist/features/harness/platform/local-apps.d.ts +36 -3
  132. package/dist/features/harness/platform/local-apps.js +81 -5
  133. package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
  134. package/dist/features/harness/platform/persona-credentials.js +69 -0
  135. package/dist/features/harness/platform/platform-drives.d.ts +61 -0
  136. package/dist/features/harness/platform/platform-drives.js +114 -0
  137. package/dist/features/harness/platform/refusal.d.ts +52 -0
  138. package/dist/features/harness/platform/refusal.js +71 -0
  139. package/dist/features/harness/platform/remote-drive.d.ts +8 -2
  140. package/dist/features/harness/platform/remote-drive.js +22 -9
  141. package/dist/features/harness/platform/script.d.ts +57 -11
  142. package/dist/features/harness/platform/script.js +144 -12
  143. package/dist/features/harness/platform/server-driver.d.ts +17 -4
  144. package/dist/features/harness/platform/server-driver.js +29 -26
  145. package/dist/features/harness/platform-config.d.ts +38 -5
  146. package/dist/features/harness/platform-config.js +93 -8
  147. package/dist/features/harness/route-guess.d.ts +13 -0
  148. package/dist/features/harness/route-guess.js +49 -0
  149. package/dist/features/harness/script/script-run.d.ts +30 -1
  150. package/dist/features/harness/script/script-run.js +355 -48
  151. package/dist/features/license/license.d.ts +2 -2
  152. package/dist/features/visual/step-shots.d.ts +31 -0
  153. package/dist/features/visual/step-shots.js +231 -0
  154. package/dist/features/visual/visual-tools.js +22 -14
  155. package/dist/hooks/client-directory.d.ts +18 -0
  156. package/dist/hooks/client-directory.js +57 -0
  157. package/dist/hooks/coding-agents.d.ts +34 -0
  158. package/dist/hooks/coding-agents.js +117 -0
  159. package/dist/hooks/driven-by.d.ts +21 -2
  160. package/dist/hooks/driven-by.js +42 -2
  161. package/dist/hooks/hook-bus.d.ts +4 -0
  162. package/dist/hooks/hook-bus.js +18 -0
  163. package/dist/index.d.ts +1 -0
  164. package/dist/index.js +26 -183
  165. package/dist/judgement/capsule/capsule-store.d.ts +2 -2
  166. package/dist/judgement/capsule/causal-summary.d.ts +15 -1
  167. package/dist/judgement/capsule/causal-summary.js +33 -0
  168. package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
  169. package/dist/judgement/runs/artifact/build-verification-run.js +20 -4
  170. package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
  171. package/dist/judgement/runs/artifact/gap-summary.js +12 -3
  172. package/dist/judgement/runs/artifact/replay-mapping.js +25 -9
  173. package/dist/judgement/runs/artifact/run-store.d.ts +13 -1
  174. package/dist/judgement/runs/artifact/run-store.js +175 -8
  175. package/dist/judgement/runs/context-tools.js +59 -1
  176. package/dist/judgement/runs/default-run-id.d.ts +3 -0
  177. package/dist/judgement/runs/default-run-id.js +16 -0
  178. package/dist/judgement/runs/drive-run.d.ts +53 -5
  179. package/dist/judgement/runs/drive-run.js +234 -20
  180. package/dist/judgement/runs/resume.d.ts +44 -0
  181. package/dist/judgement/runs/resume.js +137 -0
  182. package/dist/judgement/runs/reticle-runner.js +5 -2
  183. package/dist/judgement/runs/runner-port.d.ts +5 -1
  184. package/dist/judgement/runs/runner-port.js +8 -2
  185. package/dist/judgement/runs/verification-sync.d.ts +2 -0
  186. package/dist/judgement/runs/verification-sync.js +36 -15
  187. package/dist/language/flows/decision.js +2 -1
  188. package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
  189. package/dist/language/flows/fields/flow-secret-field.js +25 -2
  190. package/dist/language/flows/flow-anchor.d.ts +7 -2
  191. package/dist/language/flows/flow-anchor.js +10 -3
  192. package/dist/language/flows/flow-intent.d.ts +1 -0
  193. package/dist/language/flows/flow-journey.d.ts +6 -0
  194. package/dist/language/flows/flow-journey.js +20 -0
  195. package/dist/language/flows/flow-learning.d.ts +4 -0
  196. package/dist/language/flows/flow-learning.js +37 -3
  197. package/dist/language/flows/flow-replay-run.js +16 -18
  198. package/dist/language/flows/flow-replay-types.d.ts +1 -1
  199. package/dist/language/flows/flow-replay.d.ts +18 -1
  200. package/dist/language/flows/flow-replay.js +22 -7
  201. package/dist/language/flows/flow-result.d.ts +22 -1
  202. package/dist/language/flows/flow-result.js +47 -2
  203. package/dist/language/flows/flow-step-runners.d.ts +2 -0
  204. package/dist/language/flows/flow-step-runners.js +12 -6
  205. package/dist/language/flows/flow-store-for-session.js +5 -1
  206. package/dist/language/flows/flow-tools.d.ts +10 -0
  207. package/dist/language/flows/flow-tools.js +61 -62
  208. package/dist/language/flows/flows.d.ts +16 -1
  209. package/dist/language/flows/flows.js +69 -10
  210. package/dist/language/flows/narrow-write.d.ts +7 -0
  211. package/dist/language/flows/narrow-write.js +36 -1
  212. package/dist/language/flows/recording/recording-backtrack.js +14 -1
  213. package/dist/language/flows/recording/tape/recordings.d.ts +29 -1
  214. package/dist/language/flows/recording/tape/recordings.js +60 -10
  215. package/dist/language/flows/replay.js +29 -6
  216. package/dist/language/flows/single-replay.d.ts +7 -0
  217. package/dist/language/flows/single-replay.js +26 -0
  218. package/dist/language/flows/verify-change-tools.d.ts +1 -0
  219. package/dist/language/flows/verify-change-tools.js +200 -193
  220. package/dist/memory/cloud/cloud-config.d.ts +10 -1
  221. package/dist/memory/cloud/cloud-config.js +19 -5
  222. package/dist/memory/cloud/cloud-sync.js +3 -3
  223. package/dist/memory/cloud/harness-config.d.ts +49 -0
  224. package/dist/memory/cloud/harness-config.js +16 -20
  225. package/dist/memory/cloud/memory-scope.d.ts +17 -0
  226. package/dist/memory/cloud/memory-scope.js +22 -0
  227. package/dist/memory/cloud/sync-cycle.d.ts +32 -4
  228. package/dist/memory/cloud/sync-cycle.js +113 -12
  229. package/dist/memory/cloud/sync-daemon.d.ts +11 -0
  230. package/dist/memory/cloud/sync-daemon.js +114 -4
  231. package/dist/memory/cloud/sync-disk.js +26 -2
  232. package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
  233. package/dist/memory/cloud/sync-screenshots.js +105 -0
  234. package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
  235. package/dist/memory/cloud/unsynced-roots.js +166 -0
  236. package/dist/memory/impact/account-watch.d.ts +16 -1
  237. package/dist/memory/impact/account-watch.js +26 -1
  238. package/dist/memory/impact/impact-store.d.ts +1 -4
  239. package/dist/memory/impact/impact-store.js +32 -42
  240. package/dist/memory/intent/inline-intent.d.ts +1 -0
  241. package/dist/memory/intent/intent-shard-store.js +16 -4
  242. package/dist/memory/intent/intent-shard.d.ts +12 -0
  243. package/dist/memory/intent/intent-store.d.ts +1 -0
  244. package/dist/memory/intent/intent-tools.js +42 -2
  245. package/dist/memory/intent/open-intents.d.ts +6 -0
  246. package/dist/memory/intent/open-intents.js +10 -1
  247. package/dist/memory/intent/settled-words.d.ts +24 -0
  248. package/dist/memory/intent/settled-words.js +130 -0
  249. package/dist/memory/journal/drive-flow.d.ts +16 -0
  250. package/dist/memory/journal/drive-flow.js +34 -2
  251. package/dist/memory/journal/drive-run-flush.d.ts +2 -0
  252. package/dist/memory/journal/drive-run-flush.js +20 -5
  253. package/dist/memory/journal/journal-recorder.js +46 -3
  254. package/dist/memory/journal/on-disk/retention.d.ts +5 -0
  255. package/dist/memory/journal/on-disk/retention.js +27 -2
  256. package/dist/memory/journal/on-disk/workspace-tiers.js +2 -0
  257. package/dist/memory/journal/session-end.d.ts +15 -1
  258. package/dist/memory/journal/session-end.js +38 -7
  259. package/dist/memory/project/artifact-root-resolver.js +33 -5
  260. package/dist/memory/project/artifact-root.d.ts +4 -3
  261. package/dist/memory/project/artifact-root.js +2 -2
  262. package/dist/memory/project/dir/reticle-dir.d.ts +14 -0
  263. package/dist/memory/project/dir/reticle-dir.js +23 -2
  264. package/dist/memory/project/link-directory.d.ts +15 -0
  265. package/dist/memory/project/link-directory.js +44 -0
  266. package/dist/memory/project/root-claims.d.ts +6 -0
  267. package/dist/memory/project/root-claims.js +39 -0
  268. package/dist/memory/project/session-root.d.ts +2 -0
  269. package/dist/memory/project/session-root.js +9 -1
  270. package/dist/memory/project/sync-status.d.ts +46 -0
  271. package/dist/memory/project/sync-status.js +135 -1
  272. package/dist/memory/recall/memory-tools.js +46 -3
  273. package/dist/memory/recall/platform-memory.d.ts +34 -0
  274. package/dist/memory/recall/platform-memory.js +139 -0
  275. package/dist/memory/recall/project-memory.js +36 -8
  276. package/dist/portal/bridge/bridge.d.ts +9 -5
  277. package/dist/portal/bridge/bridge.js +66 -23
  278. package/dist/portal/bridge/harness-request.d.ts +23 -0
  279. package/dist/portal/bridge/harness-request.js +27 -0
  280. package/dist/portal/bridge/wrong-path.d.ts +17 -0
  281. package/dist/portal/bridge/wrong-path.js +35 -0
  282. package/dist/portal/input/network-mock-tools.js +11 -2
  283. package/dist/portal/input/real-input.d.ts +2 -0
  284. package/dist/portal/input/real-input.js +13 -10
  285. package/dist/portal/pool/browser-pool.d.ts +48 -0
  286. package/dist/portal/pool/browser-pool.js +120 -2
  287. package/dist/portal/pool/context-permissions.d.ts +33 -0
  288. package/dist/portal/pool/context-permissions.js +33 -0
  289. package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
  290. package/dist/portal/pool/lease-marker-stamp.js +49 -0
  291. package/dist/portal/pool/playwright-launcher.d.ts +27 -5
  292. package/dist/portal/pool/playwright-launcher.js +59 -14
  293. package/dist/portal/pool/pool-contract.d.ts +21 -1
  294. package/dist/portal/session/agent-link.d.ts +8 -0
  295. package/dist/portal/session/agent-link.js +17 -0
  296. package/dist/portal/session/command-payload.js +6 -1
  297. package/dist/portal/session/daemon-answers.d.ts +3 -3
  298. package/dist/portal/session/daemon-answers.js +4 -2
  299. package/dist/portal/session/dev-server/dev-command.d.ts +9 -0
  300. package/dist/portal/session/dev-server/dev-command.js +27 -1
  301. package/dist/portal/session/facts/observed-state.d.ts +16 -18
  302. package/dist/portal/session/facts/observed-state.js +46 -3
  303. package/dist/portal/session/human/live-control.d.ts +9 -2
  304. package/dist/portal/session/human/live-control.js +12 -3
  305. package/dist/portal/session/live-control-tools.js +82 -13
  306. package/dist/portal/session/narration-args.d.ts +5 -0
  307. package/dist/portal/session/narration-args.js +17 -0
  308. package/dist/portal/session/no-session-diagnosis.d.ts +1 -1
  309. package/dist/portal/session/no-session-diagnosis.js +31 -9
  310. package/dist/portal/session/no-session-next-action.d.ts +20 -0
  311. package/dist/portal/session/no-session-next-action.js +134 -61
  312. package/dist/portal/session/no-session-watch.js +179 -22
  313. package/dist/portal/session/presence/session-lease.js +1 -1
  314. package/dist/portal/session/recorded-gaps.d.ts +97 -0
  315. package/dist/portal/session/recorded-gaps.js +251 -0
  316. package/dist/portal/session/session-health.d.ts +11 -0
  317. package/dist/portal/session/session-health.js +3 -1
  318. package/dist/portal/session/session-info.js +2 -1
  319. package/dist/portal/session/session-manager.d.ts +18 -0
  320. package/dist/portal/session/session-manager.js +54 -3
  321. package/dist/portal/session/session-successor.d.ts +8 -0
  322. package/dist/portal/session/session-successor.js +1 -1
  323. package/dist/portal/session/session.d.ts +15 -28
  324. package/dist/portal/session/session.js +60 -61
  325. package/dist/portal/session/timing/command-timeout.js +25 -1
  326. package/dist/portal/session/timing/wall-clock.d.ts +1 -1
  327. package/dist/portal/session/timing/wall-clock.js +1 -1
  328. package/dist/portal/session/zero-install-first-call.d.ts +31 -0
  329. package/dist/portal/session/zero-install-first-call.js +88 -0
  330. package/dist/start-options.d.ts +7 -1
  331. package/dist/start-wiring.d.ts +63 -0
  332. package/dist/start-wiring.js +169 -0
  333. package/dist/status-payload.d.ts +13 -1
  334. package/dist/status-payload.js +13 -1
  335. package/dist/surface/http-server.js +7 -1
  336. package/dist/surface/mcp/attach-memory.d.ts +1 -0
  337. package/dist/surface/mcp/attach-memory.js +12 -1
  338. package/dist/surface/mcp/mcp-proxy.d.ts +4 -0
  339. package/dist/surface/mcp/mcp-proxy.js +29 -8
  340. package/dist/surface/mcp/mcp.d.ts +23 -0
  341. package/dist/surface/mcp/mcp.js +34 -4
  342. package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
  343. package/dist/surface/mcp/proxy/drive-calls.js +27 -0
  344. package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
  345. package/dist/surface/mcp/server-instructions.js +22 -14
  346. package/dist/surface/mcp/shared-params.js +1 -1
  347. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  348. package/dist/surface/mcp/surface-vocabulary.js +1 -0
  349. package/dist/surface/tools/account-tool.d.ts +22 -0
  350. package/dist/surface/tools/account-tool.js +157 -0
  351. package/dist/surface/tools/act/act-danger.d.ts +11 -0
  352. package/dist/surface/tools/act/act-danger.js +38 -1
  353. package/dist/surface/tools/act/act-observation.d.ts +33 -0
  354. package/dist/surface/tools/act/act-observation.js +57 -0
  355. package/dist/surface/tools/act/act-preflight.js +25 -2
  356. package/dist/surface/tools/act/act-target.js +8 -3
  357. package/dist/surface/tools/act/resolve-target.d.ts +1 -1
  358. package/dist/surface/tools/act/resolve-target.js +18 -3
  359. package/dist/surface/tools/act/screen-request.d.ts +34 -0
  360. package/dist/surface/tools/act/screen-request.js +113 -0
  361. package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
  362. package/dist/surface/tools/act/sequence-grade.js +13 -2
  363. package/dist/surface/tools/act-sequence-tool.js +136 -27
  364. package/dist/surface/tools/act-tools.d.ts +0 -9
  365. package/dist/surface/tools/act-tools.js +48 -34
  366. package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
  367. package/dist/surface/tools/agent/app-notes-learning.js +80 -0
  368. package/dist/surface/tools/agent/app-notes.d.ts +4 -0
  369. package/dist/surface/tools/agent/app-notes.js +48 -0
  370. package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
  371. package/dist/surface/tools/agent/build-staleness.js +62 -0
  372. package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
  373. package/dist/surface/tools/agent/compact-envelope.js +374 -0
  374. package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
  375. package/dist/surface/tools/agent/for-the-agent.js +44 -0
  376. package/dist/surface/tools/agent/notices.d.ts +50 -0
  377. package/dist/surface/tools/agent/notices.js +152 -0
  378. package/dist/surface/tools/agent/reteach.d.ts +11 -0
  379. package/dist/surface/tools/agent/reteach.js +50 -0
  380. package/dist/surface/tools/args/alias-args.d.ts +5 -0
  381. package/dist/surface/tools/args/alias-args.js +10 -0
  382. package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
  383. package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
  384. package/dist/surface/tools/assert/assert-source.d.ts +15 -0
  385. package/dist/surface/tools/assert/assert-source.js +40 -0
  386. package/dist/surface/tools/assert/assert-verdict.js +25 -15
  387. package/dist/surface/tools/browser-tools.js +43 -1
  388. package/dist/surface/tools/chat-drives.d.ts +75 -0
  389. package/dist/surface/tools/chat-drives.js +265 -21
  390. package/dist/surface/tools/coverage-tools.d.ts +2 -10
  391. package/dist/surface/tools/coverage-tools.js +2 -53
  392. package/dist/surface/tools/dynamic-tools.js +29 -1
  393. package/dist/surface/tools/empty-session-list.d.ts +10 -0
  394. package/dist/surface/tools/empty-session-list.js +45 -5
  395. package/dist/surface/tools/error-recovery.d.ts +8 -0
  396. package/dist/surface/tools/error-recovery.js +69 -5
  397. package/dist/surface/tools/explore-tools.d.ts +23 -2
  398. package/dist/surface/tools/explore-tools.js +377 -74
  399. package/dist/surface/tools/feedback-tools.d.ts +2 -3
  400. package/dist/surface/tools/feedback-tools.js +4 -3
  401. package/dist/surface/tools/gap-novelty.d.ts +3 -1
  402. package/dist/surface/tools/gap-novelty.js +8 -1
  403. package/dist/surface/tools/harness-explore.d.ts +52 -73
  404. package/dist/surface/tools/harness-explore.js +177 -146
  405. package/dist/surface/tools/harness-gate.d.ts +88 -0
  406. package/dist/surface/tools/harness-gate.js +235 -0
  407. package/dist/surface/tools/harness-hunt.d.ts +43 -0
  408. package/dist/surface/tools/harness-hunt.js +215 -0
  409. package/dist/surface/tools/harness-plan.d.ts +5 -0
  410. package/dist/surface/tools/harness-plan.js +7 -0
  411. package/dist/surface/tools/harness-script.d.ts +22 -0
  412. package/dist/surface/tools/harness-script.js +256 -38
  413. package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
  414. package/dist/surface/tools/harness-tab-guard.js +27 -0
  415. package/dist/surface/tools/harness-toolset.d.ts +8 -0
  416. package/dist/surface/tools/harness-toolset.js +27 -4
  417. package/dist/surface/tools/human-notes.d.ts +16 -0
  418. package/dist/surface/tools/human-notes.js +27 -0
  419. package/dist/surface/tools/invoke-tool.d.ts +5 -2
  420. package/dist/surface/tools/invoke-tool.js +220 -93
  421. package/dist/surface/tools/lease-hint.js +26 -5
  422. package/dist/surface/tools/lease-permissions.d.ts +33 -0
  423. package/dist/surface/tools/lease-permissions.js +73 -0
  424. package/dist/surface/tools/lease-readiness.d.ts +38 -0
  425. package/dist/surface/tools/lease-readiness.js +57 -0
  426. package/dist/surface/tools/lease-seed.d.ts +20 -0
  427. package/dist/surface/tools/lease-seed.js +45 -1
  428. package/dist/surface/tools/lease-tools.d.ts +10 -1
  429. package/dist/surface/tools/lease-tools.js +119 -69
  430. package/dist/surface/tools/live-call-text.js +2 -2
  431. package/dist/surface/tools/merge-tools.js +42 -2
  432. package/dist/surface/tools/merged-name-redirect.js +4 -0
  433. package/dist/surface/tools/next-step.d.ts +11 -1
  434. package/dist/surface/tools/next-step.js +73 -18
  435. package/dist/surface/tools/observe-tools.js +43 -5
  436. package/dist/surface/tools/platform-moment.d.ts +60 -0
  437. package/dist/surface/tools/platform-moment.js +132 -4
  438. package/dist/surface/tools/read-tools.js +65 -14
  439. package/dist/surface/tools/reticle-result.d.ts +120 -0
  440. package/dist/surface/tools/reticle-result.js +364 -0
  441. package/dist/surface/tools/tool-kit.d.ts +68 -2
  442. package/dist/surface/tools/tool-kit.js +73 -1
  443. package/dist/surface/tools/tools.js +25 -13
  444. package/dist/telemetry/account-envelope.d.ts +7 -0
  445. package/dist/telemetry/account-envelope.js +61 -0
  446. package/dist/telemetry/agent-client.d.ts +15 -0
  447. package/dist/telemetry/agent-client.js +57 -0
  448. package/dist/telemetry/app-instrumented.js +9 -2
  449. package/dist/telemetry/argument-shape.d.ts +7 -0
  450. package/dist/telemetry/argument-shape.js +86 -1
  451. package/dist/telemetry/automation-hint.d.ts +2 -3
  452. package/dist/telemetry/automation-hint.js +33 -8
  453. package/dist/telemetry/cli-telemetry.js +7 -2
  454. package/dist/telemetry/daemon-telemetry.js +3 -0
  455. package/dist/telemetry/error-fingerprint.d.ts +10 -1
  456. package/dist/telemetry/error-fingerprint.js +61 -5
  457. package/dist/telemetry/feedback-cli.d.ts +0 -13
  458. package/dist/telemetry/feedback-cli.js +0 -77
  459. package/dist/telemetry/feedback.d.ts +13 -0
  460. package/dist/telemetry/feedback.js +183 -17
  461. package/dist/telemetry/harness-run.d.ts +18 -0
  462. package/dist/telemetry/harness-run.js +80 -0
  463. package/dist/telemetry/hud-events.d.ts +16 -0
  464. package/dist/telemetry/hud-events.js +27 -0
  465. package/dist/telemetry/hud-metrics.d.ts +27 -6
  466. package/dist/telemetry/hud-metrics.js +99 -25
  467. package/dist/telemetry/init-telemetry.js +2 -2
  468. package/dist/telemetry/install-source.d.ts +13 -25
  469. package/dist/telemetry/install-source.js +44 -3
  470. package/dist/telemetry/mcp-connection.d.ts +0 -14
  471. package/dist/telemetry/mcp-connection.js +5 -1
  472. package/dist/telemetry/onboarding-firsts.d.ts +32 -0
  473. package/dist/telemetry/onboarding-firsts.js +81 -6
  474. package/dist/telemetry/platform-linked.d.ts +5 -0
  475. package/dist/telemetry/platform-linked.js +32 -0
  476. package/dist/telemetry/project-profile.js +5 -3
  477. package/dist/telemetry/proxy-telemetry.js +4 -1
  478. package/dist/telemetry/session-metrics.d.ts +15 -4
  479. package/dist/telemetry/session-metrics.js +88 -52
  480. package/dist/telemetry/telemetry.d.ts +26 -2
  481. package/dist/telemetry/telemetry.js +102 -21
  482. package/dist/telemetry/verification-of.d.ts +3 -1
  483. package/dist/telemetry/verification-of.js +12 -4
  484. package/dist/wire-journal.d.ts +2 -2
  485. package/dist/wire-journal.js +16 -5
  486. package/docs/README.md +2 -2
  487. package/docs/actions.mdx +6 -3
  488. package/docs/adapters.md +1 -1
  489. package/docs/agent-cheatsheet.md +24 -27
  490. package/docs/architecture.md +5 -5
  491. package/docs/autodrive.md +13 -12
  492. package/docs/benchmarks.md +5 -5
  493. package/docs/best-practices.mdx +27 -9
  494. package/docs/capabilities.mdx +14 -16
  495. package/docs/cli/affected.mdx +1 -1
  496. package/docs/cli/cloud.mdx +54 -75
  497. package/docs/cli/doctor.mdx +8 -3
  498. package/docs/cli/drive.mdx +12 -12
  499. package/docs/cli/feedback.mdx +3 -3
  500. package/docs/cli/gate.mdx +7 -2
  501. package/docs/cli/help.mdx +50 -55
  502. package/docs/cli/init.mdx +32 -15
  503. package/docs/cli/license.mdx +12 -8
  504. package/docs/cli/mcp.mdx +4 -4
  505. package/docs/cli/open.mdx +3 -3
  506. package/docs/cli/report.mdx +4 -2
  507. package/docs/cli/restart.mdx +2 -2
  508. package/docs/cli/rollback.mdx +14 -8
  509. package/docs/cli/serve.mdx +4 -4
  510. package/docs/cli/setup.mdx +23 -0
  511. package/docs/cli/status.mdx +13 -4
  512. package/docs/cli/stop.mdx +67 -8
  513. package/docs/cli/try.mdx +62 -0
  514. package/docs/cli/update.mdx +4 -3
  515. package/docs/cli/verify.mdx +17 -6
  516. package/docs/cli/version.mdx +3 -2
  517. package/docs/cli.mdx +110 -41
  518. package/docs/debugging.md +3 -3
  519. package/docs/deploy-checks.md +1 -1
  520. package/docs/desktop.mdx +5 -3
  521. package/docs/enterprise.md +11 -10
  522. package/docs/environment.mdx +5 -2
  523. package/docs/faq.mdx +3 -3
  524. package/docs/first-drive.md +5 -4
  525. package/docs/flows.md +35 -27
  526. package/docs/for-agents.md +4 -5
  527. package/docs/frameworks.mdx +32 -21
  528. package/docs/getting-started.md +66 -64
  529. package/docs/human-control.md +15 -14
  530. package/docs/index.mdx +15 -43
  531. package/docs/install-agentic.mdx +22 -37
  532. package/docs/install-manual.mdx +22 -12
  533. package/docs/instrumentation.mdx +5 -3
  534. package/docs/integration-patterns.md +7 -7
  535. package/docs/license-activation.mdx +1 -1
  536. package/docs/multi-agent-testing.md +24 -6
  537. package/docs/packages/babel-plugin.mdx +4 -2
  538. package/docs/packages/browser.mdx +16 -10
  539. package/docs/packages/core.mdx +12 -5
  540. package/docs/packages/electron.mdx +3 -2
  541. package/docs/packages/engine.mdx +1 -1
  542. package/docs/packages/eslint-plugin.mdx +18 -12
  543. package/docs/packages/next.mdx +8 -7
  544. package/docs/packages/open-verification.mdx +1 -1
  545. package/docs/packages/server.mdx +5 -5
  546. package/docs/packages/tauri.mdx +8 -8
  547. package/docs/packages/test.mdx +4 -10
  548. package/docs/packages/vite-plugin.mdx +11 -4
  549. package/docs/packages.mdx +11 -15
  550. package/docs/platform-integration.md +6 -5
  551. package/docs/predicates.mdx +40 -24
  552. package/docs/quickstart.mdx +58 -29
  553. package/docs/required-checks.md +8 -7
  554. package/docs/reticle-manual.md +15 -5
  555. package/docs/skill-file.mdx +17 -79
  556. package/docs/skill-setup.md +9 -9
  557. package/docs/skill.md +200 -0
  558. package/docs/system-map.md +7 -7
  559. package/docs/telemetry.md +22 -24
  560. package/docs/testing.md +4 -4
  561. package/docs/token-efficiency.md +12 -12
  562. package/docs/tools/act-and-wait.mdx +9 -2
  563. package/docs/tools/act-sequence.mdx +6 -7
  564. package/docs/tools/act.mdx +2 -2
  565. package/docs/tools/assert.mdx +18 -17
  566. package/docs/tools/console.mdx +9 -9
  567. package/docs/tools/inspect.mdx +16 -7
  568. package/docs/tools/look.mdx +24 -0
  569. package/docs/tools/navigate.mdx +5 -3
  570. package/docs/tools/network.mdx +2 -2
  571. package/docs/tools/observe.mdx +6 -6
  572. package/docs/tools/overview.mdx +34 -33
  573. package/docs/tools/query.mdx +5 -5
  574. package/docs/tools/session-and-feedback.mdx +13 -4
  575. package/docs/tools/sessions.mdx +8 -8
  576. package/docs/tools/snapshot.mdx +6 -5
  577. package/docs/tools/state.mdx +9 -9
  578. package/docs/tools/tools-and-run.mdx +26 -24
  579. package/docs/tools/verify.mdx +28 -0
  580. package/docs/tools/wait-for.mdx +8 -4
  581. package/docs/troubleshooting.mdx +21 -6
  582. package/docs/usage.md +51 -45
  583. package/docs/use-cases.mdx +3 -3
  584. package/docs/vs-chrome-devtools-mcp.mdx +9 -6
  585. package/docs/vs-playwright-mcp.mdx +3 -1
  586. package/docs/what-is-recorded.md +7 -5
  587. package/docs/what-is-reticle.mdx +2 -2
  588. package/docs/why-reticle.mdx +1 -1
  589. package/package.json +6 -5
  590. package/dist/command/cli/demo/demo-page.d.ts +0 -78
  591. package/dist/command/cli/demo/demo-page.js +0 -249
  592. package/dist/command/cli/demo/demo-run.d.ts +0 -38
  593. package/dist/command/cli/demo/demo-run.js +0 -138
  594. package/dist/judgement/hunt/hunt-report.d.ts +0 -49
  595. package/dist/judgement/hunt/hunt-report.js +0 -84
  596. package/dist/language/flows/buddy-status.d.ts +0 -25
  597. package/dist/language/flows/buddy-status.js +0 -32
  598. package/dist/language/flows/change/watch-batcher.d.ts +0 -18
  599. package/dist/language/flows/change/watch-batcher.js +0 -19
  600. package/dist/memory/cloud/harness-offer.d.ts +0 -44
  601. package/dist/memory/cloud/harness-offer.js +0 -145
  602. package/dist/surface/tools/feature-capture.d.ts +0 -151
  603. package/dist/surface/tools/feature-capture.js +0 -235
  604. package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
  605. package/dist/surface/tools/tool-hit-rate.js +0 -90
  606. package/dist/telemetry/harness-drive.d.ts +0 -4
  607. package/dist/telemetry/harness-drive.js +0 -35
  608. package/dist/telemetry/identify.d.ts +0 -22
  609. package/dist/telemetry/identify.js +0 -73
  610. package/docs/cli/capsules.mdx +0 -70
  611. package/docs/cli/hunt.mdx +0 -73
  612. package/docs/cli/identify.mdx +0 -76
  613. package/docs/cli/kill.mdx +0 -91
  614. package/docs/cli/watch.mdx +0 -62
@@ -1,6 +1,7 @@
1
- import { FlowErrorCode } from '@reticlehq/core';
1
+ import { FlowErrorCode, NO_SUCH_FLOW, ReplayStatus, ReticleTool, Verified } from '@reticlehq/core';
2
2
  import { SAFE_SEGMENT_PATTERN } from '@reticlehq/core';
3
3
  import { FlowParseNote } from './flow-expect-grammar.js';
4
+ import { isAmbient } from './recording/tape/recordings.js';
4
5
  /**
5
6
  * A project id that is safe to put in a path, or nothing.
6
7
  *
@@ -36,7 +37,7 @@ export function flowErrorMessage(code, detail) {
36
37
  case FlowErrorCode.INVALID_NAME:
37
38
  return 'invalid flow name — use a single safe segment (letters/digits/-/_), no path separators';
38
39
  case FlowErrorCode.NOT_FOUND:
39
- return 'no such flow on disk — run reticle_flow{action:"list"} to see saved flows';
40
+ return `${NO_SUCH_FLOW} — run reticle_flow{action:"list"} to see saved flows`;
40
41
  case FlowErrorCode.PARSE_FAILED:
41
42
  return FlowParseNote.MALFORMED;
42
43
  case FlowErrorCode.NO_RECORDING:
@@ -49,3 +50,47 @@ export function flowErrorMessage(code, detail) {
49
50
  return (detail ?? 'the step to resume at is not in this flow — pass a 0-based index or a step id');
50
51
  }
51
52
  }
53
+ /**
54
+ * A replay as one check of the session that ran it: held when it replayed clean, failed when a step
55
+ * failed, undecided when it drifted or its flow asserts nothing. A session whose only work was a
56
+ * failing replay ended "nothing was checked" beside the replay's own failure.
57
+ */
58
+ export function replayVerdictEffect(name, status, assertsNothing) {
59
+ const verified = ReplayStatus.OK === status && !assertsNothing
60
+ ? Verified.YES
61
+ : ReplayStatus.ERROR === status
62
+ ? Verified.NO
63
+ : Verified.UNKNOWN;
64
+ return {
65
+ claim: `the flow "${name}" replays and its checks hold`,
66
+ verified,
67
+ declaredBeforeActing: true,
68
+ grade: REPLAY_GRADE,
69
+ };
70
+ }
71
+ /** The grade a replay's check is recorded with. */
72
+ const REPLAY_GRADE = 'flow';
73
+ /** Record a replay as one check of the session that ran it. A session double without a journal is skipped. */
74
+ export function noteReplayCheck(session, name, status, assertsNothing) {
75
+ session.recordAction?.(ReticleTool.FLOW_REPLAY, { flowName: name }, replayVerdictEffect(name, status, assertsNothing));
76
+ }
77
+ /**
78
+ * The active recording a refused `reticle_flow_save` was most likely about: the one it named, or
79
+ * the only one running when it named none and nothing is stopped yet.
80
+ */
81
+ // A running recording has no compiled program yet, so a save refusal names it (#1551).
82
+ export function stillRecording(active, name, explicitName, held) {
83
+ if (active.includes(name) && !isAmbient(name))
84
+ return name;
85
+ // The ambient tape is the daemon's own, never one the agent started, so it is neither named nor
86
+ // counted: with a named recording beside it, that one is still the only candidate.
87
+ const opened = active.filter((recording) => !isAmbient(recording));
88
+ if ((explicitName === undefined || 0 === explicitName.length) && 0 === held.length) {
89
+ return 1 === opened.length ? opened[0] : undefined;
90
+ }
91
+ return undefined;
92
+ }
93
+ export function stillRecordingMessage(recording) {
94
+ return (`'${recording}' is still recording, and flow_save only saves a stopped recording. ` +
95
+ `Stop it first with reticle_record{action:"stop", recordingName:"${recording}"}, then save again.`);
96
+ }
@@ -1,6 +1,8 @@
1
1
  import { AnchorKind, type FlowAnchor, type FlowReplayResult, type FlowStep, type FlowStepResult, type Predicate } from '@reticlehq/core';
2
2
  import type { FlowReplaySession, Sleep } from './flow-replay-types.js';
3
3
  import { type Reresolved } from './flow-anchor.js';
4
+ /** The live act_sequence tool gives each step this transport budget when none was supplied. */
5
+ export declare const DEFAULT_SEQUENCE_STEP_TIMEOUT_MS = 8000;
4
6
  export declare function runRoleStep(session: FlowReplaySession, step: FlowStep, index: number, anchor: Extract<FlowAnchor, {
5
7
  kind: typeof AnchorKind.ROLE;
6
8
  }>, confirmDangerous: boolean, sleep: Sleep): Promise<FlowStepResult>;
@@ -16,6 +16,8 @@ import { anchorFieldName } from './flows.js';
16
16
  import { ambiguityDrift, refFor, anchorLabel, componentLabel, componentQueryArgs, expectElementDrift, reresolved, resolveQuery, testidDrift, } from './flow-anchor.js';
17
17
  import { nearestRoleName } from './role-anchor-nearest.js';
18
18
  import { roleDriftReason } from '../../judgement/outcome/role-drift-reason.js';
19
+ /** The live act_sequence tool gives each step this transport budget when none was supplied. */
20
+ export const DEFAULT_SEQUENCE_STEP_TIMEOUT_MS = 8_000;
19
21
  /** Query args for a NAMED role anchor — the handle that identifies an instance, not a JSX site. */
20
22
  function roleQueryArgs(anchor) {
21
23
  return {
@@ -134,11 +136,8 @@ function replayDestructiveActionHint(rawError) {
134
136
  return (`${rawError} — this is a flow REPLAY: a step's own args.confirmDangerous is stripped before ` +
135
137
  'dispatch and never persists, so editing the flow file does nothing. Pass it at the call level ' +
136
138
  `instead: ${ReticleTool.FLOW_REPLAY} { confirmDangerous: true } acknowledges every flagged step ` +
137
- // `reticle_verify{action:"flows"}` (the batch suite runner) has no such argument today — naming
138
- // it here would repeat the exact defect this message exists to fix, so the honest statement is
139
- // that a flow with a flagged step currently has to be replayed on its own to pass it.
140
- 'for this run. The batch reticle_verify{action:"flows"} has no equivalent option yet, so a flow ' +
141
- 'with a flagged step has to be replayed on its own to acknowledge it.');
139
+ 'for this run, as do reticle_verify{action:"flows", confirmDangerous: true} for a suite and ' +
140
+ '`reticle verify <url> --confirm-dangerous` from the command line.');
142
141
  }
143
142
  /**
144
143
  * Dispatch one already-resolved step. Shared so every anchor kind runs the action the same way —
@@ -467,7 +466,14 @@ dynamic = new Set()) {
467
466
  const send = async (steps) => {
468
467
  session.beginAction?.(ReticleTool.FLOW_REPLAY, { steps: steps.length });
469
468
  try {
470
- return await session.command(ReticleCommand.ACT_SEQUENCE, { steps });
469
+ // A recorded zero means "do not wait for the element", not "abort the whole batch now".
470
+ // Keep the transport alive long enough for the sequence's default per-step budget, and give
471
+ // an omitted timeout the same aggregate budget as the live tool's independent step waits.
472
+ const perStepTimeout = undefined === step.timeoutMs || 0 === step.timeoutMs
473
+ ? DEFAULT_SEQUENCE_STEP_TIMEOUT_MS
474
+ : step.timeoutMs;
475
+ const timeoutMs = perStepTimeout * steps.length;
476
+ return await session.command(ReticleCommand.ACT_SEQUENCE, { steps }, timeoutMs);
471
477
  }
472
478
  finally {
473
479
  session.finishAction?.();
@@ -29,5 +29,9 @@ export function flowsForSession(deps, target) {
29
29
  if (resolved === undefined || resolved.root === deps.reticleRoot) {
30
30
  return { flows: deps.flows, root: deps.reticleRoot };
31
31
  }
32
- return { flows: new FlowStore(deps.fs, resolved.root, { now: deps.now }), root: resolved.root };
32
+ const onWrote = deps.onRunPersisted;
33
+ return {
34
+ flows: new FlowStore(deps.fs, resolved.root, { now: deps.now }, onWrote === undefined ? {} : { onWrote }),
35
+ root: resolved.root,
36
+ };
33
37
  }
@@ -10,6 +10,16 @@ export { replayNamedFlow } from './flow-replay-run.js';
10
10
  * point; flows that need another page carry their own startPath.
11
11
  */
12
12
  export declare function leasableAppUrl(deps: ToolDeps, sessionId: string | undefined): string | undefined;
13
+ /**
14
+ * The live session's own URL — what the fixture port must be looked up by.
15
+ *
16
+ * NOT `leasableAppUrl`. That returns an ORIGIN, and the input provider finds a page by comparing
17
+ * full URLs, so an origin matched no page: `suiteFixtureSeed` answered `undefined` on every run and
18
+ * every leased flow booted cold while the code read as if it were seeded. Silent, because every
19
+ * refusal on that path is deliberately silent — a fixture is an optimisation, and a suite must not
20
+ * die when one is unavailable.
21
+ */
22
+ export declare function sessionUrlOf(deps: ToolDeps, sessionId: string | undefined): string | undefined;
13
23
  /** A flow whose leased context never came up is a suite ERROR, never a silent pass. */
14
24
  export declare function leaseFailureReplay(name: string, error: string | undefined): FlowReplayResult;
15
25
  export declare const FLOW_TOOLS: ToolDef[];
@@ -1,19 +1,24 @@
1
+ import { CALL_SECRETS_ARG, callSecretsArg } from './fields/flow-secret-field.js';
1
2
  import { flowAuthor } from './flow-author.js';
2
3
  import { FLOW_MUTATE_TOOL } from './flow-mutate-tools.js';
3
- import { seedSchema } from '../../surface/tools/args/numeric-bounds.js';
4
+ import { seedSchema, workerCountSchema } from '../../surface/tools/args/numeric-bounds.js';
5
+ import { confirmDangerousArg } from '../../surface/tools/args/confirm-dangerous-arg.js';
4
6
  import { z } from 'zod';
5
7
  import { emptyFlowRefusal } from './empty-flow.js';
6
- import { aliasParam } from '../../surface/tools/args/alias-args.js';
8
+ import { aliasParam, aliasUsed } from '../../surface/tools/args/alias-args.js';
9
+ import { stillRecording, stillRecordingMessage } from './flow-result.js';
10
+ import { resolveAnnotateTarget } from './annotate-notes/annotate-target.js';
7
11
  import { FlowErrorCode, RecordedSaveError, ReplayStatus, SuiteIsolation, } from '@reticlehq/core';
8
12
  import { recordSuiteFlakes } from './suite/suite-flakes.js';
9
13
  import { AppRuntime, ReticleTool } from '@reticlehq/core';
10
- import { asNumber, asString, mutationTargetsFor, perturbationFor } from '@reticlehq/core';
11
- import { workerCountSchema } from '../../surface/tools/args/numeric-bounds.js';
14
+ import { asNumber, asString } from '@reticlehq/core';
12
15
  import { log } from '../../log.js';
13
16
  import { cloudFetch, syncFlowToCloud, SyncOutcome } from '../../memory/cloud/cloud-sync.js';
14
17
  import { mapWithConcurrency, resolveConcurrency } from './suite/parallel-suite.js';
15
- import { acquireLeasedSession, sessionPerturbationPort, suiteFixtureSeed, } from '../../surface/tools/lease-tools.js';
18
+ import { acquireLeasedSession, suiteFixtureSeed } from '../../surface/tools/lease-tools.js';
16
19
  import { homedir } from 'node:os';
20
+ import { withShotRun } from '../../judgement/runs/default-run-id.js';
21
+ import { defaultRunId } from '../../judgement/runs/default-run-id.js';
17
22
  import { resolveProjectCloud } from '../../memory/cloud/cloud-config.js';
18
23
  import { buildSuiteVerdict } from './decision.js';
19
24
  import { classifyFlowAssertions, flattenSteps } from './flow-classify.js';
@@ -24,10 +29,12 @@ import { flowsForSession } from './flow-store-for-session.js';
24
29
  import { rootForTarget, sessionTarget } from '../../memory/project/session-root.js';
25
30
  import { originOf } from '../../portal/session/session-manager.js';
26
31
  import { resolveSuiteSelection } from './suite-selection.js';
27
- import { replayNamedFlow, flowErrorMessage, latestRecordedFlow, sessionProjectId, } from './flow-replay-run.js';
32
+ import { flowErrorMessage, latestRecordedFlow, sessionProjectId } from './flow-replay-run.js';
28
33
  import { persistAndSyncVerificationRun, } from '../../judgement/runs/verification-sync.js';
29
34
  import { runServerVerify } from './suite/server-verify.js';
30
35
  import { healFlow } from './heal-run.js';
36
+ import { RESULT_FIELD, resultField, suiteResult } from '../../surface/tools/reticle-result.js';
37
+ import { replayOneFlow } from './single-replay.js';
31
38
  export { replayNamedFlow } from './flow-replay-run.js';
32
39
  import { replayAndLearn } from './flow-learning.js';
33
40
  /**
@@ -81,7 +88,7 @@ export function leasableAppUrl(deps, sessionId) {
81
88
  * refusal on that path is deliberately silent — a fixture is an optimisation, and a suite must not
82
89
  * die when one is unavailable.
83
90
  */
84
- function sessionUrlOf(deps, sessionId) {
91
+ export function sessionUrlOf(deps, sessionId) {
85
92
  try {
86
93
  return deps.sessions.resolve(sessionId).url;
87
94
  }
@@ -118,15 +125,17 @@ export const FLOW_TOOLS = [
118
125
  FLOW_MUTATE_TOOL,
119
126
  {
120
127
  name: ReticleTool.FLOW_SAVE,
121
- description: 'Persist the last/active recording (by name) as a git-checked, anchor-resolved flow at .reticle/flows/<name>.json. Each step is bound to a SEMANTIC anchor (testid/role/signal), never a volatile ref; steps without a resolvable testid are kept with degraded:true (a "add a data-testid here" marker) rather than dropped. Returns { name, stepCount, degraded, empty, assertions, warning? } — `assertions.grade` is asserted | presence-only | assertion-free: a flow that only acts (or only checks element presence) will pass even if the feature breaks, so when grade is not "asserted" follow assertions.warning and add a consequence assertion via reticle_annotate (assert-signal / assert-net / success-state). `warning` is present when the recording left a page and returned to it: replay has no navigation steps, so the next click will look for a control on a page the tab is no longer on.',
128
+ description: 'Persist a STOPPED recording (by name) as a git-checked, anchor-resolved flow at .reticle/flows/<name>.json. A recording that is still running is refused: stop it first with reticle_record{action:"stop"}, and the refusal names the exact call. Each step is bound to a SEMANTIC anchor (testid/role/signal), never a volatile ref; steps without a resolvable testid are kept with degraded:true (a "add a data-testid here" marker) rather than dropped. Returns { name, stepCount, degraded, empty, assertions, warning? } — `assertions.grade` is asserted | presence-only | assertion-free: a flow that only acts (or only checks element presence) will pass even if the feature breaks, so when grade is not "asserted" follow assertions.warning and add a consequence assertion via reticle_annotate (assert-signal / assert-net / success-state). `warning` is present when the recording left a page and returned to it: replay has no navigation steps, so the next click will look for a control on a page the tab is no longer on.',
122
129
  inputSchema: {
123
130
  flow: z
124
131
  .string()
125
132
  .optional()
126
133
  .describe('Alias for `flowName` (the name reticle_annotate uses).'),
134
+ name: z.string().optional().describe('Alias for `flowName`.'),
127
135
  flowName: z
128
136
  .string()
129
- .describe('Which RECORDING to save — the name reticle_record{start} was called with. This is a lookup key, not the filename: pass `saveAs` to choose that.'),
137
+ .optional()
138
+ .describe('Which RECORDING to save — the name reticle_record{start} was called with ("last-journey" for the reticle_act {steps} just proved). Omit when only one is stopped. This is a lookup key, not the filename: pass `saveAs` to choose that.'),
130
139
  saveAs: z
131
140
  .string()
132
141
  .optional()
@@ -162,6 +171,7 @@ export const FLOW_TOOLS = [
162
171
  })
163
172
  .optional(),
164
173
  intentGap: INTENT_GAP_FIELD,
174
+ normalized: z.array(z.string()).optional(),
165
175
  warning: z
166
176
  .string()
167
177
  .optional()
@@ -170,7 +180,12 @@ export const FLOW_TOOLS = [
170
180
  code: z.string().optional(),
171
181
  },
172
182
  handler: (deps, args) => {
173
- const name = asString(aliasParam(args, 'flowName', ['flow'])['flowName']) ?? '';
183
+ const aliases = ['flow', 'name', 'recordingName'];
184
+ const normalized = aliasUsed(args, 'flowName', aliases);
185
+ const explicitName = asString(aliasParam(args, 'flowName', aliases)['flowName']);
186
+ // The one stopped recording when the call names none — the same defaulting record{stop} and
187
+ // annotate use. With several, nothing is picked: the refusal below names them.
188
+ const name = resolveAnnotateTarget(explicitName, deps.recordings.compiled());
174
189
  const program = deps.recordings.getCompiled(name);
175
190
  if (program === undefined) {
176
191
  /*
@@ -183,6 +198,13 @@ export const FLOW_TOOLS = [
183
198
  * before the names were compared.
184
199
  */
185
200
  const held = deps.recordings.compiled();
201
+ const running = stillRecording(deps.recordings.active(), name, explicitName, held);
202
+ if (running !== undefined) {
203
+ return Promise.resolve({
204
+ error: stillRecordingMessage(running),
205
+ code: FlowErrorCode.NO_RECORDING,
206
+ });
207
+ }
186
208
  return Promise.resolve({
187
209
  error: 0 === held.length
188
210
  ? flowErrorMessage(FlowErrorCode.NO_RECORDING)
@@ -251,6 +273,7 @@ export const FLOW_TOOLS = [
251
273
  const invoked = loaded.ok ? await loadInvoked(flows, loaded.value, projectId) : new Map();
252
274
  return loaded.ok
253
275
  ? {
276
+ ...(0 === normalized.length ? {} : { normalized }),
254
277
  ...res.value,
255
278
  ...(path === undefined ? {} : { path }),
256
279
  assertions: classifyFlowAssertions(loaded.value, invoked),
@@ -391,10 +414,8 @@ export const FLOW_TOOLS = [
391
414
  flowName: z
392
415
  .string()
393
416
  .describe('Flow file name (without .json extension) from reticle_flow{action:"list"}.'),
394
- confirmDangerous: z
395
- .boolean()
396
- .optional()
397
- .describe('Set true to allow destructive controls during this replay only.'),
417
+ [CALL_SECRETS_ARG]: callSecretsArg,
418
+ confirmDangerous: confirmDangerousArg('this replay'),
398
419
  from: z
399
420
  .union([z.number().int().nonnegative(), z.string().min(1)])
400
421
  .optional()
@@ -409,10 +430,10 @@ export const FLOW_TOOLS = [
409
430
  at: z
410
431
  .number()
411
432
  .int()
412
- .positive()
433
+ .nonnegative()
413
434
  .max(MAX_REPLAY_STEP)
414
435
  .optional()
415
- .describe('Continue at this step on the page as it is (after another flow ran `to` it).'),
436
+ .describe('Continue at this step on the page as it is (after another flow ran `to` it); 0 replays the whole flow on the page the flow before it left, with no reload.'),
416
437
  sweep: z
417
438
  .boolean()
418
439
  .optional()
@@ -430,10 +451,12 @@ export const FLOW_TOOLS = [
430
451
  .array(z.unknown())
431
452
  .optional()
432
453
  .describe('Present when `seed` was given: which endpoints were delayed, and by how long.'),
454
+ [RESULT_FIELD]: z.string().optional(),
433
455
  // The flow's name — always present in FlowReplayResult (the description promises `{ name, … }`),
434
456
  // but omitted here, so a validating profile stripped it and a replay result arrived anonymous.
435
457
  name: z.string(),
436
458
  status: z.string().describe('ok | drift | error | unverifiable'),
459
+ runId: z.string().optional().describe('The run this replay was written down as.'),
437
460
  steps: z.array(z.unknown()),
438
461
  proposals: z.array(z.unknown()).optional(),
439
462
  deviation: z
@@ -487,42 +510,7 @@ export const FLOW_TOOLS = [
487
510
  },
488
511
  // The decision envelope is attached by replayNamedFlow only on drift/fail (clean pass stays
489
512
  // token-flat). Single-flow replay and whole-suite verify share that one implementation.
490
- handler: async (deps, args) => {
491
- const seed = asNumber(args['seed']);
492
- if (seed === undefined)
493
- return await replayAndLearn(deps, args);
494
- /*
495
- * Seeded chaos, and always put the page back.
496
- *
497
- * The targets are the endpoints the FLOW declared it depends on, so the perturbation lands on
498
- * traffic the flow claims to care about rather than on something it never mentioned. No
499
- * declared endpoints means nothing to slow, and the replay runs clean rather than pretending
500
- * a seed did something.
501
- *
502
- * `finally` is not tidiness: a page left slowed is damage the NEXT run inherits, and it would
503
- * inherit it as a mystery — the same rule the mutation loop had to earn.
504
- */
505
- const sessionId = asString(args['sessionId']);
506
- const target = sessionTarget(deps, sessionId);
507
- const loaded = await flowsForSession(deps, target)
508
- .flows.load(asString(args['flowName']) ?? asString(args['flow']) ?? '', target.projectId)
509
- .catch(() => null);
510
- const targets = loaded !== null && loaded.ok ? mutationTargetsFor(loaded.value) : [];
511
- const rules = perturbationFor(seed, targets);
512
- const port = 0 === rules.length
513
- ? undefined
514
- : await sessionPerturbationPort(deps.realInput, leasableAppUrl(deps, sessionId));
515
- if (port === undefined)
516
- return replayAndLearn(deps, args);
517
- try {
518
- await port.slow(rules);
519
- const replay = await replayNamedFlow(deps, args);
520
- return { ...replay, perturbed: rules };
521
- }
522
- finally {
523
- await port.clear().catch(() => undefined);
524
- }
525
- },
513
+ handler: (deps, args) => replayOneFlow(deps, args, leasableAppUrl(deps, asString(args['sessionId']))),
526
514
  },
527
515
  {
528
516
  name: ReticleTool.FLOW_VERIFY,
@@ -532,6 +520,7 @@ export const FLOW_TOOLS = [
532
520
  'whatChanged, whereInSource, nextAction }] } plus `flaky` (flows seen to pass AND fail on unchanged code) — passing flows are counted, only failures carry ' +
533
521
  'detail (the actionable fix). One call replaces N hand-driven replays.',
534
522
  inputSchema: {
523
+ [CALL_SECRETS_ARG]: callSecretsArg,
535
524
  names: z
536
525
  .array(z.string())
537
526
  .optional()
@@ -543,6 +532,7 @@ export const FLOW_TOOLS = [
543
532
  parallel: workerCountSchema
544
533
  .optional()
545
534
  .describe('Run this many flows at once, each in its own isolated leased browser context (needs the browser pool). Clamped to the pool capacity and the flow count. Omit for the sequential single-tab run.'),
535
+ confirmDangerous: confirmDangerousArg('this run of the suite'),
546
536
  sessionId: z
547
537
  .string()
548
538
  .optional()
@@ -550,10 +540,12 @@ export const FLOW_TOOLS = [
550
540
  },
551
541
  outputSchema: {
552
542
  status: z.string().describe('pass | fail'),
543
+ runId: z.string().optional().describe('The run this suite was written down as.'),
553
544
  total: z.number(),
554
545
  passed: z.number(),
555
546
  failed: z.number(),
556
547
  summary: z.string(),
548
+ [RESULT_FIELD]: z.string().optional(),
557
549
  failures: z.array(z.unknown()),
558
550
  isolation: z
559
551
  .string()
@@ -595,8 +587,10 @@ export const FLOW_TOOLS = [
595
587
  // feedback ask here or a validating profile strips it off the suite verdict.
596
588
  feedback_prompt: z.unknown().optional(),
597
589
  },
598
- handler: async (deps, args) => {
590
+ // The run id is minted first, so each replayed step's screenshot is filed under the run.
591
+ handler: (deps, args) => withShotRun(defaultRunId(), async () => {
599
592
  const sessionId = asString(args['sessionId']);
593
+ const confirmed = true === args['confirmDangerous'] ? { confirmDangerous: true } : {};
600
594
  // "Replay all" means all of THIS app's flows (+ legacy), not every project's on a shared daemon.
601
595
  const target = sessionTarget(deps, sessionId);
602
596
  const { projectId } = target;
@@ -652,7 +646,11 @@ export const FLOW_TOOLS = [
652
646
  const start = deps.now();
653
647
  const lease = await acquireLeasedSession(pool, deps.sessions, appUrl, projectId, seed);
654
648
  try {
655
- const replay = await replayAndLearn(deps, { flowName, sessionId: lease.sessionId });
649
+ const replay = await replayAndLearn(deps, {
650
+ flowName,
651
+ sessionId: lease.sessionId,
652
+ ...confirmed,
653
+ });
656
654
  return { replay, durationMs: deps.now() - start };
657
655
  }
658
656
  finally {
@@ -673,12 +671,14 @@ export const FLOW_TOOLS = [
673
671
  durationMs: o.ok && o.value !== undefined ? o.value.durationMs : 0,
674
672
  }));
675
673
  const flaky = await recordSuiteFlakes(deps.fs, rootForTarget(deps, target), parallelRuns);
676
- await persistAndSyncVerificationRun(deps, timed, target);
674
+ const runId = await persistAndSyncVerificationRun(deps, timed, target);
677
675
  const verdict = buildSuiteVerdict(parallelRuns, selected.knownRoutes, SuiteIsolation.PER_FLOW_LEASE);
678
676
  return {
679
677
  ...verdict,
678
+ ...(runId === undefined ? {} : { runId }),
680
679
  ...(flaky.length > 0 ? { flaky: [...flaky] } : {}),
681
680
  ...heldBack,
681
+ ...resultField(() => suiteResult(rootForTarget(deps, target), verdict, timed, runId)),
682
682
  };
683
683
  }
684
684
  // The flow FILE travels with each replay so the verdict can tell a green that verified
@@ -696,7 +696,7 @@ export const FLOW_TOOLS = [
696
696
  // (1 of 2 flows). Restoring two thirds of a session is worse than restoring none.
697
697
  for (const flowName of requested) {
698
698
  const start = deps.now();
699
- const replay = await replayAndLearn(deps, { flowName, sessionId });
699
+ const replay = await replayAndLearn(deps, { flowName, sessionId, ...confirmed });
700
700
  const loaded = await flowsForSession(deps, target)
701
701
  .flows.load(flowName, projectId)
702
702
  .catch(() => null);
@@ -714,17 +714,19 @@ export const FLOW_TOOLS = [
714
714
  // one an agent uses was the blind half.
715
715
  const flaky = await recordSuiteFlakes(deps.fs, rootForTarget(deps, target), runs);
716
716
  // Emit the consolidated run artifact (Runs tab) + best-effort cloud push. Never blocks the verdict.
717
- await persistAndSyncVerificationRun(deps, timed, target);
717
+ const runId = await persistAndSyncVerificationRun(deps, timed, target);
718
718
  const verdict = buildSuiteVerdict(runs, selected.knownRoutes, SuiteIsolation.SHARED_SESSION);
719
719
  // A flow that has both passed and failed on UNCHANGED code is a different thing from a
720
720
  // regression, and an agent that cannot tell them apart either chases a ghost or ignores a real
721
721
  // break. Present only when the ledger has seen enough runs to say so.
722
722
  return {
723
723
  ...verdict,
724
+ ...(runId === undefined ? {} : { runId }),
724
725
  ...(flaky.length > 0 ? { flaky: [...flaky] } : {}),
725
726
  ...heldBack,
727
+ ...resultField(() => suiteResult(rootForTarget(deps, target), verdict, timed, runId)),
726
728
  };
727
- },
729
+ }),
728
730
  },
729
731
  {
730
732
  name: ReticleTool.FLOW_SAVE_RECORDED,
@@ -819,10 +821,7 @@ export const FLOW_TOOLS = [
819
821
  .describe('Alias for `flowName` (the name reticle_annotate uses).'),
820
822
  flowName: z.string().describe('Flow file name to heal (from reticle_flow{action:"list"}).'),
821
823
  apply: z.boolean().optional(),
822
- confirmDangerous: z
823
- .boolean()
824
- .optional()
825
- .describe('Set true to allow destructive controls during this heal replay only.'),
824
+ confirmDangerous: confirmDangerousArg('this heal replay'),
826
825
  sessionId: z
827
826
  .string()
828
827
  .optional()
@@ -11,6 +11,14 @@ export declare function anchorForStep(args: Record<string, unknown>): {
11
11
  anchor: FlowAnchor;
12
12
  degraded: boolean;
13
13
  };
14
+ /**
15
+ * The recorded steps as they may be SHOWN: every credential fill carries the placeholder.
16
+ *
17
+ * The same rule the saved flow is written by, applied to what a tool hands back. `record stop`
18
+ * returned the compiled program verbatim, so the password a saved flow never contained went into
19
+ * the agent's transcript instead. The store keeps the real value — an in-session replay types it.
20
+ */
21
+ export declare function redactRecordedSteps(steps: readonly RecordedStep[]): RecordedStep[];
14
22
  /**
15
23
  * What the anchor CALLS the field it points at.
16
24
  *
@@ -58,7 +66,14 @@ export interface FlowAnnotations {
58
66
  /** Persists anchored flows to .reticle/flows/<name>.json. Filesystem + clock are injected. */
59
67
  export declare class FlowStore {
60
68
  #private;
61
- constructor(fs: FileSystemPort, root: string, clock: Clock);
69
+ /**
70
+ * `onWrote` is told after a save lands, so cloud sync cycles on a new flow the way it does on a
71
+ * new run (`RunStore` takes the same callback). Without it a flow saved with no run behind it
72
+ * waited for the timer, and a daemon that exited first never sent it.
73
+ */
74
+ constructor(fs: FileSystemPort, root: string, clock: Clock, opts?: {
75
+ onWrote?: () => void;
76
+ });
62
77
  /**
63
78
  * Convert a CompiledProgram (testid-normalized) into an anchored, on-disk flow + write it.
64
79
  * Optionally fold structured annotations (per-step expect, dynamic[], success) onto
@@ -1,15 +1,15 @@
1
1
  import { asFlowName, asProjectId, } from '@reticlehq/core';
2
- import { REDACTED_FILL } from './fields/flow-secret-field.js';
2
+ import { REDACTED_FILL, SECRET_NAME_ARG, secretEnvKey } from './fields/flow-secret-field.js';
3
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
6
  import { AnchorKind, DEGRADED_ANCHOR_ROLE, FLOW_FILE_VERSION, flowFileVersionFor, FlowErrorCode, FlowFileSchema, FlowStepTool, QueryBy, defaultIsSensitiveKey, } from '@reticlehq/core';
7
- import { ReticleTool } from '@reticlehq/core';
7
+ import { ReticleTool, flowJsonReplacer } 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
+ import { journeyFingerprint, journeyName, mergeJourney, routeClaims, withinJourney, } from './flow-journey.js';
13
13
  import { IntentStore } from '../../memory/intent/intent-store.js';
14
14
  import { flowDir, flowParentDir, flowPath, reticleDirPaths, isValidFlowName, } from '../../memory/project/dir/reticle-dir.js';
15
15
  import { describeFlowZodFailure, parseFlowFileText } from './flow-expect-grammar.js';
@@ -130,7 +130,32 @@ function redactSecretFill(anchor, args) {
130
130
  const field = anchorFieldName(anchor);
131
131
  if (field === undefined || !defaultIsSensitiveKey(field))
132
132
  return args;
133
- return { ...args, value: REDACTED_FILL };
133
+ // The variable that supplies it, beside the placeholder: the file alone says what to set.
134
+ return { ...args, value: REDACTED_FILL, [SECRET_NAME_ARG]: secretEnvKey(field) };
135
+ }
136
+ /**
137
+ * The recorded steps as they may be SHOWN: every credential fill carries the placeholder.
138
+ *
139
+ * The same rule the saved flow is written by, applied to what a tool hands back. `record stop`
140
+ * returned the compiled program verbatim, so the password a saved flow never contained went into
141
+ * the agent's transcript instead. The store keeps the real value — an in-session replay types it.
142
+ */
143
+ export function redactRecordedSteps(steps) {
144
+ return steps.map((step) => {
145
+ if (step.tool === ReticleTool.ACT_SEQUENCE && Array.isArray(step.args['steps'])) {
146
+ const subs = step.args['steps'].map((raw) => redactNormalizedArgs(asRecord(raw)));
147
+ return { ...step, args: { ...step.args, steps: subs } };
148
+ }
149
+ return { ...step, args: redactNormalizedArgs(step.args) };
150
+ });
151
+ }
152
+ /** One normalized act's args with its inner fill `args` redacted by the anchor it would save as. */
153
+ function redactNormalizedArgs(normalized) {
154
+ const inner = normalized['args'];
155
+ if ('object' !== typeof inner || null === inner)
156
+ return normalized;
157
+ const redacted = redactSecretFill(anchorForStep(normalized).anchor, asRecord(inner));
158
+ return redacted === inner ? normalized : { ...normalized, args: redacted };
134
159
  }
135
160
  /**
136
161
  * What the anchor CALLS the field it points at.
@@ -169,6 +194,8 @@ function buildStep(tool, anchor, action, args, degraded) {
169
194
  */
170
195
  export function recordedStepToFlowStep(step) {
171
196
  const out = anchoredStep(step);
197
+ if (step.timeoutMs !== undefined)
198
+ out.timeoutMs = step.timeoutMs;
172
199
  if (step.page !== undefined)
173
200
  out.page = step.page;
174
201
  if (step.endPage !== undefined && step.endPage !== step.page)
@@ -238,10 +265,17 @@ export class FlowStore {
238
265
  #fs;
239
266
  #root;
240
267
  #clock;
241
- constructor(fs, root, clock) {
268
+ #onWrote;
269
+ /**
270
+ * `onWrote` is told after a save lands, so cloud sync cycles on a new flow the way it does on a
271
+ * new run (`RunStore` takes the same callback). Without it a flow saved with no run behind it
272
+ * waited for the timer, and a daemon that exited first never sent it.
273
+ */
274
+ constructor(fs, root, clock, opts = {}) {
242
275
  this.#fs = fs;
243
276
  this.#root = root;
244
277
  this.#clock = clock;
278
+ this.#onWrote = opts.onWrote;
245
279
  }
246
280
  /**
247
281
  * The single byte-stable flow serializer: 2-space indent + one trailing newline. save,
@@ -250,7 +284,7 @@ export class FlowStore {
250
284
  */
251
285
  #serialize(flow) {
252
286
  const stamped = { ...flow, version: flowFileVersionFor(flow) };
253
- return `${JSON.stringify(stamped, null, JSON_INDENT)}\n`;
287
+ return `${JSON.stringify(stamped, flowJsonReplacer, JSON_INDENT)}\n`;
254
288
  }
255
289
  /**
256
290
  * Register the flow's business goal in the intent ledger and stamp the row's id onto the flow.
@@ -324,6 +358,7 @@ export class FlowStore {
324
358
  const flow = await this.#linkIntent(base);
325
359
  await this.#fs.mkdir(flowParentDir(this.#root, program.name, pid));
326
360
  await this.#fs.writeFile(flowPath(this.#root, program.name, pid), this.#serialize(flow));
361
+ this.#onWrote?.();
327
362
  return { ok: true, value: this.#summary(flow) };
328
363
  }
329
364
  /**
@@ -340,19 +375,40 @@ export class FlowStore {
340
375
  const pid = safeProjectId(projectId);
341
376
  const fresh = this.#build(program, undefined, pid);
342
377
  const saved = [];
343
- for (const name of (await this.list(pid)).filter((n) => n.startsWith(prefix))) {
378
+ const freshPrint = journeyFingerprint(fresh);
379
+ for (const name of await this.list(pid)) {
344
380
  const loaded = await this.load(name, pid);
345
- if (loaded.ok)
381
+ if (!loaded.ok)
382
+ continue;
383
+ if (name.startsWith(prefix))
346
384
  saved.push(loaded.value);
385
+ // The agent already saved this journey under its own name: one journey, one flow. A web drive
386
+ // saved its named flow and an automatic `drive-saved-<email>` beside it, every time.
387
+ else if (journeyFingerprint(loaded.value) === freshPrint) {
388
+ return { ok: true, value: this.#summary(loaded.value) };
389
+ }
347
390
  }
348
391
  const byPrint = new Map();
349
392
  for (const flow of saved) {
350
393
  const print = journeyFingerprint(flow);
351
394
  byPrint.set(print, [...(byPrint.get(print) ?? []), flow]);
352
395
  }
353
- const print = journeyFingerprint(fresh);
396
+ const print = freshPrint;
354
397
  const taken = new Map(saved.map((flow) => [flow.name, journeyFingerprint(flow)]));
355
- const named = { ...fresh, name: journeyName(fresh.name, print, taken) };
398
+ // The same name holding this journey driven with a step more or less: one file, the fuller drive.
399
+ const same = saved.find((flow) => flow.name === fresh.name && taken.get(flow.name) !== print);
400
+ if (same !== undefined && withinJourney(same, fresh))
401
+ return { ok: true, value: this.#summary(same) };
402
+ const extended = same !== undefined && withinJourney(fresh, same);
403
+ if (extended)
404
+ taken.delete(fresh.name);
405
+ const named = {
406
+ ...fresh,
407
+ ...(extended && fresh.intent === undefined && same.intent !== undefined
408
+ ? { intent: same.intent }
409
+ : {}),
410
+ name: journeyName(fresh.name, print, taken),
411
+ };
356
412
  byPrint.set(print, [...(byPrint.get(print) ?? []), named]);
357
413
  let result = named;
358
414
  for (const [groupPrint, group] of byPrint) {
@@ -370,6 +426,7 @@ export class FlowStore {
370
426
  if (groupPrint === print)
371
427
  result = merged;
372
428
  }
429
+ this.#onWrote?.();
373
430
  return { ok: true, value: this.#summary(result) };
374
431
  }
375
432
  /** Write a flow where it already lives, or into its project's directory when it is new. */
@@ -404,6 +461,7 @@ export class FlowStore {
404
461
  const valid = await this.#linkIntent(parsed.data);
405
462
  await this.#fs.mkdir(flowParentDir(this.#root, asFlowName(valid.name), pid));
406
463
  await this.#fs.writeFile(flowPath(this.#root, asFlowName(valid.name), pid), this.#serialize(valid));
464
+ this.#onWrote?.();
407
465
  return { ok: true, value: this.#summary(valid) };
408
466
  }
409
467
  /**
@@ -422,6 +480,7 @@ export class FlowStore {
422
480
  resolvePath: (n, p) => this.#resolveReadPath(asFlowName(n), p),
423
481
  write: (path, contents) => this.#fs.writeFile(path, contents),
424
482
  serialize: (flow) => this.#serialize(flow),
483
+ readRaw: (path) => this.#fs.readFile(path),
425
484
  }, name, projectId, change);
426
485
  }
427
486
  /** Write back only what a replay LEARNED. See `#changeInPlace` for why it is not `saveFlow`. */