@reticlehq/server 3.1.0 → 3.3.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 (384) hide show
  1. package/SKILL.md +30 -28
  2. package/dist/command/cli/adhoc-suite.d.ts +19 -0
  3. package/dist/command/cli/adhoc-suite.js +88 -0
  4. package/dist/command/cli/adhoc-verdict.d.ts +23 -2
  5. package/dist/command/cli/adhoc-verdict.js +18 -10
  6. package/dist/command/cli/auth/device-flow.d.ts +2 -2
  7. package/dist/command/cli/cli-doctor.js +37 -5
  8. package/dist/command/cli/cli-flow-commands.d.ts +2 -1
  9. package/dist/command/cli/cli-kill.js +1 -1
  10. package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
  11. package/dist/command/cli/cli-parse-grammar.js +7 -0
  12. package/dist/command/cli/cli-parse-verify.d.ts +2 -5
  13. package/dist/command/cli/cli-parse-verify.js +86 -13
  14. package/dist/command/cli/cli-parse.d.ts +8 -1
  15. package/dist/command/cli/cli-parse.js +38 -7
  16. package/dist/command/cli/cli-verify.d.ts +59 -0
  17. package/dist/command/cli/cli-verify.js +131 -19
  18. package/dist/command/cli/cloud-cli.js +7 -6
  19. package/dist/command/cli/cloud-kit.d.ts +0 -2
  20. package/dist/command/cli/cloud-kit.js +3 -7
  21. package/dist/command/cli/config/config-discovery.d.ts +12 -0
  22. package/dist/command/cli/config/config-discovery.js +19 -0
  23. package/dist/command/cli/doctor/doctor-rows.d.ts +12 -0
  24. package/dist/command/cli/doctor/doctor-rows.js +12 -0
  25. package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
  26. package/dist/command/cli/drive/drive-command.js +1 -1
  27. package/dist/command/cli/launch/cli-launch.d.ts +3 -23
  28. package/dist/command/cli/launch/cli-launch.js +29 -70
  29. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +33 -0
  30. package/dist/command/cli/lifecycle/daemon-lifecycle.js +198 -0
  31. package/dist/command/cli/mcp-command.js +5 -3
  32. package/dist/command/cli/ports/resolve/cli-port.d.ts +15 -1
  33. package/dist/command/cli/ports/resolve/cli-port.js +50 -2
  34. package/dist/command/cli/report-command.d.ts +16 -0
  35. package/dist/command/cli/report-command.js +97 -0
  36. package/dist/command/cli/status/status-lines.d.ts +28 -0
  37. package/dist/command/cli/status/status-lines.js +117 -0
  38. package/dist/command/cli.d.ts +1 -1
  39. package/dist/command/cli.js +29 -177
  40. package/dist/command/daemon/binding/daemon-status-probe.d.ts +32 -0
  41. package/dist/command/daemon/binding/daemon-status-probe.js +93 -0
  42. package/dist/command/daemon/binding/loopback-reach.d.ts +42 -0
  43. package/dist/command/daemon/binding/loopback-reach.js +63 -0
  44. package/dist/command/daemon/binding/port-presence.d.ts +1 -1
  45. package/dist/command/daemon/daemon-resilience.d.ts +13 -1
  46. package/dist/command/daemon/daemon-resilience.js +12 -1
  47. package/dist/command/daemon/daemon.d.ts +2 -11
  48. package/dist/command/daemon/daemon.js +8 -11
  49. package/dist/command/daemon/idle-shutdown.d.ts +2 -1
  50. package/dist/command/daemon/lifetime/heartbeat.d.ts +8 -0
  51. package/dist/command/daemon/lifetime/heartbeat.js +10 -0
  52. package/dist/command/daemon/lifetime/log-cap.d.ts +68 -0
  53. package/dist/command/daemon/lifetime/log-cap.js +115 -0
  54. package/dist/command/setup/agent-notice-memory.d.ts +16 -0
  55. package/dist/command/setup/agent-notice-memory.js +65 -0
  56. package/dist/command/setup/bringup/dev-server-wait.d.ts +1 -0
  57. package/dist/command/setup/bringup/dev-server-wait.js +16 -0
  58. package/dist/command/setup/bringup/ensure-daemon.js +1 -1
  59. package/dist/command/setup/init/init-host.js +8 -0
  60. package/dist/command/setup/init/init-runtime.js +13 -1
  61. package/dist/command/setup/node-effects.js +1 -1
  62. package/dist/command/setup/probe/page-probe.js +9 -2
  63. package/dist/command/setup/run-setup.d.ts +25 -7
  64. package/dist/command/setup/run-setup.js +51 -9
  65. package/dist/command/setup/setup-command.js +31 -3
  66. package/dist/command/setup/terminal/confirm.js +2 -1
  67. package/dist/command/update/updater.d.ts +27 -0
  68. package/dist/command/update/updater.js +35 -2
  69. package/dist/features/crawl/crawl-tools.js +3 -1
  70. package/dist/features/crawl/crawl.d.ts +2 -0
  71. package/dist/features/crawl/crawl.js +3 -1
  72. package/dist/features/harness/drive-report.d.ts +73 -0
  73. package/dist/features/harness/drive-report.js +196 -0
  74. package/dist/features/harness/driver.js +29 -8
  75. package/dist/features/harness/drivers.d.ts +19 -0
  76. package/dist/features/harness/drivers.js +19 -0
  77. package/dist/features/harness/fill-values.d.ts +67 -0
  78. package/dist/features/harness/fill-values.js +153 -0
  79. package/dist/features/harness/jev-driver.d.ts +188 -0
  80. package/dist/features/harness/jev-driver.js +661 -0
  81. package/dist/features/harness/openai-driver.d.ts +46 -0
  82. package/dist/features/harness/openai-driver.js +197 -0
  83. package/dist/features/harness/platform-config.d.ts +62 -0
  84. package/dist/features/harness/platform-config.js +97 -0
  85. package/dist/features/visual/visual-tools.js +26 -2
  86. package/dist/index.d.ts +1 -1
  87. package/dist/index.js +80 -130
  88. package/dist/judgement/capsule/capsule-store.d.ts +31 -2
  89. package/dist/judgement/capsule/capsule-store.js +58 -6
  90. package/dist/judgement/capsule/capsule.js +5 -0
  91. package/dist/judgement/capsule/causal-summary.d.ts +13 -1
  92. package/dist/judgement/capsule/causal-summary.js +7 -0
  93. package/dist/judgement/domain/domain-model.js +38 -9
  94. package/dist/judgement/domain/domain-tools.js +4 -2
  95. package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
  96. package/dist/judgement/phenomena/phenomena.d.ts +2 -1
  97. package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
  98. package/dist/judgement/runs/artifact/gap-report.js +46 -0
  99. package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
  100. package/dist/judgement/runs/artifact/gap-summary.js +85 -0
  101. package/dist/judgement/runs/artifact/render-report.js +4 -1
  102. package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
  103. package/dist/judgement/runs/artifact/to-artifact.js +1 -1
  104. package/dist/judgement/runs/context-tools.js +21 -3
  105. package/dist/judgement/runs/drive-run.js +7 -2
  106. package/dist/judgement/runs/verification-sync.d.ts +2 -2
  107. package/dist/judgement/runs/verification-sync.js +9 -2
  108. package/dist/language/flows/affected-tools.js +6 -3
  109. package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
  110. package/dist/language/flows/annotate-notes/annotate.js +66 -18
  111. package/dist/language/flows/flow-anchor.d.ts +12 -0
  112. package/dist/language/flows/flow-anchor.js +20 -0
  113. package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
  114. package/dist/language/flows/flow-expect-grammar.js +92 -11
  115. package/dist/language/flows/flow-intent.js +1 -1
  116. package/dist/language/flows/flow-learning.d.ts +3 -0
  117. package/dist/language/flows/flow-learning.js +39 -8
  118. package/dist/language/flows/flow-replay-run.d.ts +55 -10
  119. package/dist/language/flows/flow-replay-run.js +217 -58
  120. package/dist/language/flows/flow-replay-types.d.ts +9 -0
  121. package/dist/language/flows/flow-replay.d.ts +29 -11
  122. package/dist/language/flows/flow-replay.js +221 -203
  123. package/dist/language/flows/flow-result.d.ts +18 -1
  124. package/dist/language/flows/flow-result.js +18 -2
  125. package/dist/language/flows/flow-step-runners.d.ts +26 -10
  126. package/dist/language/flows/flow-step-runners.js +20 -4
  127. package/dist/language/flows/flow-store-for-session.d.ts +2 -1
  128. package/dist/language/flows/flow-success.d.ts +21 -6
  129. package/dist/language/flows/flow-success.js +50 -138
  130. package/dist/language/flows/flow-tools.js +8 -4
  131. package/dist/language/flows/flows.d.ts +15 -10
  132. package/dist/language/flows/flows.js +17 -4
  133. package/dist/language/flows/heal.d.ts +9 -1
  134. package/dist/language/flows/heal.js +24 -2
  135. package/dist/language/flows/learned-sources.d.ts +21 -0
  136. package/dist/language/flows/learned-sources.js +84 -0
  137. package/dist/language/flows/narrow-write.d.ts +4 -4
  138. package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
  139. package/dist/language/flows/recording/recording-backtrack.js +1 -1
  140. package/dist/language/flows/recording/replay-halt.d.ts +2 -1
  141. package/dist/language/flows/recording/tape/recordings.d.ts +2 -2
  142. package/dist/language/flows/replay.js +13 -9
  143. package/dist/language/flows/stores/annotation-store.d.ts +5 -5
  144. package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
  145. package/dist/language/flows/stores/flake-store.js +2 -3
  146. package/dist/language/flows/suite/coverage.d.ts +1 -1
  147. package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
  148. package/dist/language/flows/suite-selection.d.ts +15 -1
  149. package/dist/language/flows/suite-selection.js +1 -1
  150. package/dist/language/flows/verify-change-tools.js +21 -4
  151. package/dist/memory/cloud/account-state.d.ts +1 -1
  152. package/dist/memory/cloud/account-state.js +5 -4
  153. package/dist/memory/cloud/cloud-config.d.ts +9 -0
  154. package/dist/memory/cloud/cloud-config.js +9 -0
  155. package/dist/memory/cloud/cloud-sync.d.ts +7 -4
  156. package/dist/memory/cloud/cloud-sync.js +11 -6
  157. package/dist/memory/cloud/harness-config.d.ts +12 -0
  158. package/dist/memory/cloud/harness-config.js +58 -0
  159. package/dist/memory/cloud/harness-offer.d.ts +44 -0
  160. package/dist/memory/cloud/harness-offer.js +146 -0
  161. package/dist/memory/cloud/harness-switch.d.ts +11 -0
  162. package/dist/memory/cloud/harness-switch.js +53 -0
  163. package/dist/memory/cloud/sync-cycle.d.ts +12 -0
  164. package/dist/memory/cloud/sync-cycle.js +29 -4
  165. package/dist/memory/cloud/sync-daemon.d.ts +35 -2
  166. package/dist/memory/cloud/sync-daemon.js +45 -0
  167. package/dist/memory/cloud/sync-disk.js +11 -6
  168. package/dist/memory/impact/impact-recorder.d.ts +8 -0
  169. package/dist/memory/impact/impact-recorder.js +2 -1
  170. package/dist/memory/impact/impact-store.d.ts +5 -0
  171. package/dist/memory/impact/impact-store.js +25 -0
  172. package/dist/memory/intent/inline-intent.js +8 -3
  173. package/dist/memory/intent/intent-shard-store.d.ts +33 -47
  174. package/dist/memory/intent/intent-shard-store.js +253 -141
  175. package/dist/memory/intent/intent-shard.d.ts +22 -23
  176. package/dist/memory/intent/intent-shard.js +1 -1
  177. package/dist/memory/intent/intent-store.d.ts +13 -8
  178. package/dist/memory/intent/intent-store.js +46 -90
  179. package/dist/memory/intent/intent-tools.js +34 -23
  180. package/dist/memory/journal/ambient-store.js +2 -3
  181. package/dist/memory/journal/attach-journal.d.ts +11 -0
  182. package/dist/memory/journal/attach-journal.js +1 -1
  183. package/dist/memory/journal/deviation-report.js +4 -1
  184. package/dist/memory/journal/deviation-service.js +10 -2
  185. package/dist/memory/journal/drive-flow.d.ts +2 -2
  186. package/dist/memory/journal/drive-flow.js +21 -12
  187. package/dist/memory/journal/envelope-store.js +2 -3
  188. package/dist/memory/journal/journal-query.d.ts +35 -1
  189. package/dist/memory/journal/journal-query.js +46 -0
  190. package/dist/memory/journal/journal-recorder.d.ts +71 -2
  191. package/dist/memory/journal/journal-recorder.js +45 -7
  192. package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
  193. package/dist/memory/journal/on-disk/retain-policy.js +44 -0
  194. package/dist/memory/journal/on-disk/retention.d.ts +91 -2
  195. package/dist/memory/journal/on-disk/retention.js +181 -11
  196. package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
  197. package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
  198. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -2
  199. package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -38
  200. package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
  201. package/dist/memory/journal/on-disk/workspace-tiers.js +88 -0
  202. package/dist/memory/journal/route-template.d.ts +22 -0
  203. package/dist/memory/journal/route-template.js +44 -0
  204. package/dist/memory/journal/self-observation.d.ts +3 -0
  205. package/dist/memory/journal/self-observation.js +67 -0
  206. package/dist/memory/journal/session-end.d.ts +17 -4
  207. package/dist/memory/journal/session-end.js +85 -7
  208. package/dist/memory/journal/session-journal.d.ts +122 -5
  209. package/dist/memory/journal/session-journal.js +489 -32
  210. package/dist/memory/project/artifact-root-resolver.d.ts +3 -0
  211. package/dist/memory/project/artifact-root-resolver.js +89 -0
  212. package/dist/memory/project/artifact-root.d.ts +39 -2
  213. package/dist/memory/project/artifact-root.js +58 -1
  214. package/dist/memory/project/baselines.d.ts +1 -1
  215. package/dist/memory/project/dir/fill-value-store.d.ts +32 -0
  216. package/dist/memory/project/dir/fill-value-store.js +88 -0
  217. package/dist/memory/project/dir/reticle-dir.d.ts +15 -5
  218. package/dist/memory/project/dir/reticle-dir.js +11 -0
  219. package/dist/memory/project/fs/fs-port.d.ts +22 -1
  220. package/dist/memory/project/fs/fs-port.js +72 -5
  221. package/dist/memory/project/fs/write-atomic.d.ts +26 -0
  222. package/dist/memory/project/fs/write-atomic.js +48 -0
  223. package/dist/memory/project/learned-routes.d.ts +16 -2
  224. package/dist/memory/project/learned-routes.js +26 -10
  225. package/dist/memory/project/project-for-root.d.ts +30 -0
  226. package/dist/memory/project/project-for-root.js +43 -0
  227. package/dist/memory/project/project-tools.js +10 -5
  228. package/dist/memory/project/session-root.d.ts +35 -6
  229. package/dist/memory/project/session-root.js +70 -8
  230. package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
  231. package/dist/memory/recall/prior/connection-memory.js +8 -0
  232. package/dist/memory/recall/registered-projects.d.ts +2 -1
  233. package/dist/portal/bridge/auth-failure-reason.d.ts +9 -0
  234. package/dist/portal/bridge/auth-failure-reason.js +20 -3
  235. package/dist/portal/bridge/bridge.d.ts +6 -1
  236. package/dist/portal/bridge/bridge.js +36 -2
  237. package/dist/portal/input/real-input.js +2 -1
  238. package/dist/portal/pool/browser-pool.d.ts +10 -0
  239. package/dist/portal/pool/browser-pool.js +10 -0
  240. package/dist/portal/pool/playwright-launcher.js +1 -0
  241. package/dist/portal/pool/pool-contract.d.ts +2 -0
  242. package/dist/portal/pool/storage-seed.d.ts +2 -1
  243. package/dist/portal/pool/zero-install.d.ts +27 -0
  244. package/dist/portal/pool/zero-install.js +54 -0
  245. package/dist/portal/realm/web-realm.js +6 -0
  246. package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
  247. package/dist/portal/session/dev-server/served-document.d.ts +21 -0
  248. package/dist/portal/session/dev-server/served-document.js +57 -0
  249. package/dist/portal/session/facts/background-traffic.d.ts +9 -0
  250. package/dist/portal/session/facts/background-traffic.js +25 -0
  251. package/dist/portal/session/facts/session-replaced.d.ts +19 -0
  252. package/dist/portal/session/facts/session-replaced.js +22 -0
  253. package/dist/portal/session/live-control-tools.js +48 -3
  254. package/dist/portal/session/no-session-diagnosis.d.ts +1 -0
  255. package/dist/portal/session/no-session-diagnosis.js +67 -60
  256. package/dist/portal/session/no-session-next-action.js +3 -2
  257. package/dist/portal/session/no-session-watch.js +14 -10
  258. package/dist/portal/session/presence/attachment-history.d.ts +13 -2
  259. package/dist/portal/session/rebind.d.ts +18 -0
  260. package/dist/portal/session/rebind.js +38 -0
  261. package/dist/portal/session/session-health.d.ts +61 -0
  262. package/dist/portal/session/session-health.js +40 -1
  263. package/dist/portal/session/session-info.d.ts +6 -0
  264. package/dist/portal/session/session-manager.d.ts +13 -0
  265. package/dist/portal/session/session-manager.js +42 -3
  266. package/dist/portal/session/session-successor.d.ts +11 -0
  267. package/dist/portal/session/session-successor.js +14 -1
  268. package/dist/portal/session/session-verdict-facts.d.ts +28 -0
  269. package/dist/portal/session/session-verdict-facts.js +15 -0
  270. package/dist/portal/session/session.d.ts +26 -10
  271. package/dist/portal/session/session.js +48 -49
  272. package/dist/status-payload.d.ts +12 -1
  273. package/dist/status-payload.js +4 -1
  274. package/dist/surface/http-server.js +3 -5
  275. package/dist/surface/mcp/faults/mcp-outage.d.ts +2 -1
  276. package/dist/surface/mcp/faults/mcp-outage.js +20 -4
  277. package/dist/surface/mcp/mcp-proxy.d.ts +10 -1
  278. package/dist/surface/mcp/mcp-proxy.js +23 -12
  279. package/dist/surface/mcp/mcp.d.ts +10 -1
  280. package/dist/surface/mcp/mcp.js +59 -29
  281. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +15 -2
  282. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +22 -5
  283. package/dist/surface/mcp/proxy/proxy-log.d.ts +5 -11
  284. package/dist/surface/mcp/proxy/proxy-log.js +7 -22
  285. package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
  286. package/dist/surface/mcp/server-instructions.js +12 -1
  287. package/dist/surface/tools/act/act-preflight.js +6 -1
  288. package/dist/surface/tools/act/already-true.d.ts +40 -0
  289. package/dist/surface/tools/act/already-true.js +36 -0
  290. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +6 -0
  291. package/dist/surface/tools/act/navigation/navigate-arrival.js +13 -0
  292. package/dist/surface/tools/act/sequence-grade.js +13 -0
  293. package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
  294. package/dist/surface/tools/act/settle-in-flight.js +29 -6
  295. package/dist/surface/tools/act-capsule.js +49 -8
  296. package/dist/surface/tools/act-tools.js +25 -24
  297. package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
  298. package/dist/surface/tools/args/numeric-bounds.js +30 -3
  299. package/dist/surface/tools/assert/assert-grade.js +14 -2
  300. package/dist/surface/tools/assert/assert-verdict.js +74 -21
  301. package/dist/surface/tools/browser-tools.js +4 -3
  302. package/dist/surface/tools/coverage-tools.js +5 -2
  303. package/dist/surface/tools/dynamic-tools.js +43 -46
  304. package/dist/surface/tools/error-recovery.d.ts +6 -0
  305. package/dist/surface/tools/error-recovery.js +15 -0
  306. package/dist/surface/tools/explore-tools.js +80 -7
  307. package/dist/surface/tools/feedback-tools.d.ts +2 -1
  308. package/dist/surface/tools/feedback-tools.js +50 -3
  309. package/dist/surface/tools/first-sentence.d.ts +24 -0
  310. package/dist/surface/tools/first-sentence.js +0 -0
  311. package/dist/surface/tools/harness-explore.d.ts +125 -1
  312. package/dist/surface/tools/harness-explore.js +401 -15
  313. package/dist/surface/tools/harness-plan.d.ts +76 -0
  314. package/dist/surface/tools/harness-plan.js +156 -0
  315. package/dist/surface/tools/harness-toolset.js +12 -1
  316. package/dist/surface/tools/invoke-tool.js +82 -9
  317. package/dist/surface/tools/lease-hint.d.ts +8 -0
  318. package/dist/surface/tools/lease-hint.js +10 -0
  319. package/dist/surface/tools/lease-tools.d.ts +16 -28
  320. package/dist/surface/tools/lease-tools.js +53 -5
  321. package/dist/surface/tools/lineage-tools.js +1 -1
  322. package/dist/surface/tools/live-call-text.d.ts +6 -0
  323. package/dist/surface/tools/live-call-text.js +36 -2
  324. package/dist/surface/tools/observe-tools.js +69 -15
  325. package/dist/surface/tools/read/query-windowed.d.ts +1 -1
  326. package/dist/surface/tools/read/query-windowed.js +1 -1
  327. package/dist/surface/tools/read-tools.js +1 -2
  328. package/dist/surface/tools/real-input-attempt.d.ts +0 -5
  329. package/dist/surface/tools/real-input-attempt.js +1 -1
  330. package/dist/surface/tools/tool-kit.d.ts +23 -3
  331. package/dist/surface/tools/tool-kit.js +6 -1
  332. package/dist/surface/tools/tool-surface.js +17 -5
  333. package/dist/surface/tools/tools-helpers.d.ts +8 -7
  334. package/dist/surface/tools/tools-helpers.js +11 -13
  335. package/dist/surface/tools/tools.js +70 -30
  336. package/dist/telemetry/bug-found.js +32 -2
  337. package/dist/telemetry/feedback-context.js +3 -3
  338. package/dist/telemetry/feedback-local.d.ts +1 -0
  339. package/dist/telemetry/feedback-local.js +1 -3
  340. package/dist/telemetry/feedback.d.ts +1 -1
  341. package/dist/telemetry/harness-drive.d.ts +6 -0
  342. package/dist/telemetry/harness-drive.js +39 -0
  343. package/dist/telemetry/hud-metrics.d.ts +32 -0
  344. package/dist/telemetry/hud-metrics.js +95 -0
  345. package/dist/telemetry/mcp-connection.d.ts +14 -1
  346. package/dist/telemetry/mcp-connection.js +17 -2
  347. package/dist/telemetry/onboarding-firsts.d.ts +30 -1
  348. package/dist/telemetry/onboarding-firsts.js +54 -12
  349. package/dist/telemetry/session-metrics.d.ts +5 -0
  350. package/dist/telemetry/session-metrics.js +15 -1
  351. package/dist/telemetry/verification-of.js +31 -3
  352. package/dist/wire-journal.d.ts +31 -0
  353. package/dist/wire-journal.js +100 -0
  354. package/docs/README.md +1 -0
  355. package/docs/agent-cheatsheet.md +7 -3
  356. package/docs/autodrive.md +42 -2
  357. package/docs/benchmarks.md +1 -1
  358. package/docs/capabilities.mdx +1 -1
  359. package/docs/cli/cloud.mdx +11 -9
  360. package/docs/cli/doctor.mdx +1 -0
  361. package/docs/cli/init.mdx +1 -0
  362. package/docs/cli/report.mdx +44 -0
  363. package/docs/cli/status.mdx +35 -20
  364. package/docs/cli.mdx +8 -18
  365. package/docs/desktop.mdx +1 -1
  366. package/docs/faq.mdx +1 -1
  367. package/docs/flows.md +11 -0
  368. package/docs/frameworks.mdx +113 -1
  369. package/docs/install-agentic.mdx +36 -0
  370. package/docs/install-manual.mdx +1 -1
  371. package/docs/packages/tauri.mdx +1 -1
  372. package/docs/packages.mdx +1 -1
  373. package/docs/predicates.mdx +144 -4
  374. package/docs/system-map.md +6 -4
  375. package/docs/telemetry.md +2 -1
  376. package/docs/token-efficiency.md +2 -2
  377. package/docs/tools/overview.mdx +1 -1
  378. package/docs/tools/query.mdx +10 -10
  379. package/docs/troubleshooting.mdx +8 -2
  380. package/docs/usage.md +2 -2
  381. package/docs/what-is-recorded.md +62 -0
  382. package/package.json +5 -5
  383. package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
  384. package/dist/judgement/outcome/predicate-to-expect.js +0 -148
@@ -1,4 +1,4 @@
1
- import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICATE_PREFIX, flowExpectHasConsequence, flowExpectIsPresenceOnly, } from '@reticlehq/core';
1
+ import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICATE_PREFIX, flowExpectHasConsequence, flowExpectIsPresenceOnly, PredicateKind, } from '@reticlehq/core';
2
2
  /**
3
3
  * The PURE compiler at the heart of the annotation facet. A structured annotation +
4
4
  * the current captured-step count → an AnnotateOutcome (a result envelope + the patch to apply to
@@ -14,35 +14,71 @@ import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICA
14
14
  * compilation is explicitly FUTURE — an NL string never reaches here (AnnotationSchema
15
15
  * rejects it upstream; the tool maps that to UNKNOWN_KIND). No NL parser exists or is faked.
16
16
  */
17
+ /**
18
+ * A network annotation as a predicate, `count` included.
19
+ *
20
+ * Written once because it is compiled on two paths — a step gate and a flow's success oracle — and
21
+ * the first version of this migration wrote it twice and dropped `count` from both. The schema's own
22
+ * comment says why that matters: presence says the request fired, cardinality catches the
23
+ * double-submit that fired it twice, and `count` is the point of the annotation.
24
+ *
25
+ * A counted assertion also gates on `settled`. A wait-until-true evaluator satisfies `count: 1` the
26
+ * instant the FIRST request lands, which is before a duplicate arrives — so without the gate the
27
+ * guard passes on exactly the bug it exists to catch.
28
+ */
29
+ function netPredicate(net) {
30
+ const clause = {
31
+ kind: PredicateKind.NET,
32
+ };
33
+ if (net.method !== undefined)
34
+ clause.method = net.method;
35
+ if (net.urlContains !== undefined)
36
+ clause.urlContains = net.urlContains;
37
+ if (net.status !== undefined)
38
+ clause.status = net.status;
39
+ if (net.count === undefined)
40
+ return clause;
41
+ clause.count = net.count;
42
+ return { kind: PredicateKind.ALL_OF, predicates: [{ kind: PredicateKind.SETTLED }, clause] };
43
+ }
17
44
  export function compileAnnotation(a, stepCount) {
18
45
  switch (a.kind) {
19
46
  case AnnotationKind.ASSERT_SIGNAL: {
20
47
  if (0 === stepCount)
21
48
  return noStep();
22
- const expect = { signal: a.name };
49
+ const signal = {
50
+ kind: PredicateKind.SIGNAL,
51
+ name: a.name,
52
+ };
23
53
  if (a.dataMatches !== undefined)
24
- expect.signalData = a.dataMatches;
25
- return stepPatch(a, stepCount, expect);
54
+ signal.dataMatches = a.dataMatches;
55
+ return stepPatch(a, stepCount, signal);
26
56
  }
27
57
  case AnnotationKind.ASSERT_VISIBLE: {
28
58
  if (0 === stepCount)
29
59
  return noStep();
30
- return stepPatch(a, stepCount, { element: { testid: a.testid } });
60
+ return stepPatch(a, stepCount, {
61
+ kind: PredicateKind.ELEMENT,
62
+ query: { testid: a.testid },
63
+ });
31
64
  }
32
65
  case AnnotationKind.ASSERT_STATE: {
33
66
  if (0 === stepCount)
34
67
  return noStep();
35
- const state = { path: a.statePath };
68
+ const state = {
69
+ kind: PredicateKind.STATE,
70
+ path: a.statePath,
71
+ };
36
72
  if (a.store !== undefined)
37
73
  state.store = a.store;
38
74
  if (a.equals !== undefined)
39
75
  state.equals = a.equals;
40
- return stepPatch(a, stepCount, { state });
76
+ return stepPatch(a, stepCount, state);
41
77
  }
42
78
  case AnnotationKind.ASSERT_NET: {
43
79
  if (0 === stepCount)
44
80
  return noStep();
45
- return stepPatch(a, stepCount, { net: a.net });
81
+ return stepPatch(a, stepCount, netPredicate(a.net));
46
82
  }
47
83
  case AnnotationKind.MARK_DYNAMIC:
48
84
  return {
@@ -53,39 +89,51 @@ export function compileAnnotation(a, stepCount) {
53
89
  // Precedence: signal > state > net > console > testid (a consequence end-condition beats a
54
90
  // presence check). None of them → MISSING_FIELD.
55
91
  if (a.signal !== undefined) {
56
- return flowSuccess(a, { signal: a.signal });
92
+ return flowSuccess(a, { kind: PredicateKind.SIGNAL, name: a.signal });
57
93
  }
58
94
  if (a.statePath !== undefined) {
59
- const state = { path: a.statePath };
95
+ const state = {
96
+ kind: PredicateKind.STATE,
97
+ path: a.statePath,
98
+ };
60
99
  if (a.store !== undefined)
61
100
  state.store = a.store;
62
101
  if (a.equals !== undefined)
63
102
  state.equals = a.equals;
64
- if (a.hold !== undefined)
65
- state.hold = a.hold;
66
- return flowSuccess(a, { state });
103
+ // `hold` was a v1 flag meaning "this must STILL be true after the page settles". A predicate
104
+ // says that by composing: the settle gate is a clause, not a boolean on another clause.
105
+ return flowSuccess(a, true === a.hold
106
+ ? { kind: PredicateKind.ALL_OF, predicates: [{ kind: PredicateKind.SETTLED }, state] }
107
+ : state);
67
108
  }
68
109
  if (a.net !== undefined) {
69
- return flowSuccess(a, { net: a.net });
110
+ return flowSuccess(a, netPredicate(a.net));
70
111
  }
71
112
  if (a.console !== undefined) {
72
- return flowSuccess(a, { console: a.console });
113
+ return flowSuccess(a, {
114
+ kind: PredicateKind.CONSOLE,
115
+ ...(a.console.level === undefined ? {} : { level: a.console.level }),
116
+ ...(a.console.absent === undefined ? {} : { absent: a.console.absent }),
117
+ });
73
118
  }
74
119
  if (a.testid !== undefined) {
75
- return flowSuccess(a, { element: { testid: a.testid } });
120
+ return flowSuccess(a, { kind: PredicateKind.ELEMENT, query: { testid: a.testid } });
76
121
  }
77
122
  // Last, and above only MISSING_FIELD. An element is a locator and text is content; where a
78
123
  // caller offered both, the locator is the more stable anchor. But text beats nothing at all,
79
124
  // which is what an app whose only observable is a rendered value used to get (#811).
80
125
  if (a.text !== undefined) {
81
- const text = { contains: a.text.contains };
126
+ const text = {
127
+ kind: PredicateKind.TEXT,
128
+ contains: a.text.contains,
129
+ };
82
130
  if (a.text.scope !== undefined)
83
131
  text.scope = a.text.scope;
84
132
  if (a.text.absent !== undefined)
85
133
  text.absent = a.text.absent;
86
134
  if (a.text.visible !== undefined)
87
135
  text.visible = a.text.visible;
88
- return flowSuccess(a, { text });
136
+ return flowSuccess(a, text);
89
137
  }
90
138
  return { result: { ok: false, code: AnnotationErrorCode.MISSING_FIELD } };
91
139
  }
@@ -28,6 +28,18 @@ export declare function nearestTestid(missing: string, present: string[]): strin
28
28
  export declare function nearestIsAmbiguous(missing: string, present: string[]): boolean;
29
29
  /** Build the legible-drift record for a testid anchor that resolved to zero live elements. */
30
30
  export declare function testidDrift(value: string, hint: QueryEmptyHint | undefined): Drift;
31
+ /**
32
+ * Build the drift for a step whose anchor matched MORE THAN ONE live element.
33
+ *
34
+ * `ambiguous: true` is set because that is exactly what it is, and it is the field heal already
35
+ * reads to refuse an auto-rebind -- an ambiguous drift must never be healed to an arbitrary pick,
36
+ * which is the same reasoning that made this a drift instead of a note in the first place.
37
+ *
38
+ * `nearest` is null on purpose. There is no nearest: the anchor was found, several times over, and
39
+ * proposing one of the matches as "the" match would re-make the guess this reason exists to stop.
40
+ * The fix is a NARROWER anchor, and only a human or an agent looking at the page can choose it.
41
+ */
42
+ export declare function ambiguousAnchorDrift(value: string, matches: number): Drift;
31
43
  /**
32
44
  * Build the drift for a step whose `expect.element` testid was absent after the action ran.
33
45
  *
@@ -139,6 +139,26 @@ export function testidDrift(value, hint) {
139
139
  drift.ambiguous = true;
140
140
  return drift;
141
141
  }
142
+ /**
143
+ * Build the drift for a step whose anchor matched MORE THAN ONE live element.
144
+ *
145
+ * `ambiguous: true` is set because that is exactly what it is, and it is the field heal already
146
+ * reads to refuse an auto-rebind -- an ambiguous drift must never be healed to an arbitrary pick,
147
+ * which is the same reasoning that made this a drift instead of a note in the first place.
148
+ *
149
+ * `nearest` is null on purpose. There is no nearest: the anchor was found, several times over, and
150
+ * proposing one of the matches as "the" match would re-make the guess this reason exists to stop.
151
+ * The fix is a NARROWER anchor, and only a human or an agent looking at the page can choose it.
152
+ */
153
+ export function ambiguousAnchorDrift(value, matches) {
154
+ return {
155
+ reasonKind: DriftReason.ANCHOR_AMBIGUOUS,
156
+ reason: `testid "${value}" matched ${String(matches)} live elements — which one the recording meant is not decidable; anchor this step to something unique`,
157
+ anchor: value,
158
+ nearest: null,
159
+ ambiguous: true,
160
+ };
161
+ }
142
162
  /**
143
163
  * Build the drift for a step whose `expect.element` testid was absent after the action ran.
144
164
  *
@@ -18,9 +18,8 @@ export declare const FlowParseNote: {
18
18
  readonly NOT_JSON: "flow file is not valid JSON — fix the syntax or regenerate it with reticle_flow_save";
19
19
  readonly MALFORMED: "flow file is malformed — fix or regenerate it with reticle_flow_save";
20
20
  readonly UNSUPPORTED_SHAPE: "valid JSON, unsupported expect shape";
21
- readonly UNENFORCED: "valid JSON, but this expect uses a predicate kind a saved flow cannot enforce (settled, route, animation, anyOf, not)";
22
21
  };
23
- export type CoerceExpectResult = {
22
+ type CoerceExpectResult = {
24
23
  ok: true;
25
24
  value: unknown;
26
25
  } | {
@@ -31,3 +30,4 @@ export declare function coerceFlowExpect(raw: unknown): CoerceExpectResult;
31
30
  export declare function coerceFlowFileExpects(raw: unknown): CoerceExpectResult;
32
31
  export declare function describeFlowZodFailure(error: ZodError): string;
33
32
  export declare function parseFlowFileText(text: string): FlowResult<FlowFile>;
33
+ export {};
@@ -11,14 +11,11 @@
11
11
  * re-save writes the canonical shape. A kind a saved flow cannot enforce is refused, not stripped
12
12
  * to an empty expect that would grade asserted-while-unchecked.
13
13
  */
14
- import { FlowErrorCode, FlowFileSchema } from '@reticlehq/core';
15
- import { PredicateSchema } from '@reticlehq/engine/question/predicate/predicate.js';
16
- import { predicateToExpect } from '../../judgement/outcome/predicate-to-expect.js';
14
+ import { FlowExpectSchema, FlowPredicateSchema, READABLE_FLOW_VERSIONS, FlowErrorCode, FlowFileSchema, } from '@reticlehq/core';
17
15
  export const FlowParseNote = {
18
16
  NOT_JSON: 'flow file is not valid JSON — fix the syntax or regenerate it with reticle_flow_save',
19
17
  MALFORMED: 'flow file is malformed — fix or regenerate it with reticle_flow_save',
20
18
  UNSUPPORTED_SHAPE: 'valid JSON, unsupported expect shape',
21
- UNENFORCED: 'valid JSON, but this expect uses a predicate kind a saved flow cannot enforce (settled, route, animation, anyOf, not)',
22
19
  };
23
20
  function isRecord(value) {
24
21
  return 'object' === typeof value && null !== value && !Array.isArray(value);
@@ -43,20 +40,44 @@ function flattenSignalObject(raw) {
43
40
  }
44
41
  export function coerceFlowExpect(raw) {
45
42
  if (isRecord(raw) && 'kind' in raw) {
46
- const parsed = PredicateSchema.safeParse(raw);
43
+ const parsed = FlowPredicateSchema.safeParse(raw);
47
44
  if (!parsed.success) {
48
45
  return { ok: false, detail: describePredicateExpectFailure(parsed.error) };
49
46
  }
50
- const expect = predicateToExpect(parsed.data);
51
- if (undefined === expect)
52
- return { ok: false, detail: FlowParseNote.UNENFORCED };
53
- return { ok: true, value: expect };
47
+ // Kept AS the predicate. There is nothing to convert to any more, so nothing to refuse for
48
+ // being inexpressible — the shape an agent wrote is the shape the file holds.
49
+ return { ok: true, value: parsed.data };
54
50
  }
55
51
  if (!isRecord(raw))
56
52
  return { ok: true, value: raw };
57
- return { ok: true, value: flattenSignalObject(raw) };
53
+ const flattened = flattenSignalObject(raw);
54
+ /*
55
+ * A v1 expect is validated HERE so a misspelling is named.
56
+ *
57
+ * The schema accepts both formats as a union, and a union rejection reports every branch's
58
+ * complaint at once — so `{ signal: 'x', signl: 'typo' }` came back as "flow file is malformed"
59
+ * without the word `signl` in it. The file was still refused, which is the half that matters, but
60
+ * the reader was left to find a one-character typo by eye. Checking the v1 shape on its own, on
61
+ * the path that already knows this is a v1 expect, puts the key back in the message.
62
+ */
63
+ const asV1 = FlowExpectSchema.safeParse(flattened);
64
+ if (!asV1.success) {
65
+ return { ok: false, detail: describePredicateExpectFailure(asV1.error) };
66
+ }
67
+ return { ok: true, value: flattened };
58
68
  }
59
69
  function describePredicateExpectFailure(error) {
70
+ const refused = error.issues.find((one) => 'custom' === one.code);
71
+ if (refused !== undefined)
72
+ return `${FlowParseNote.UNSUPPORTED_SHAPE}: ${refused.message}`;
73
+ // An unrecognized key FIRST: a strict object reports it on the object's own path with the names
74
+ // in `keys`, so reading `path[0]` misses the one thing worth saying. A misspelling is the failure
75
+ // this whole check exists to catch, and naming it is the difference between a fix and a hunt.
76
+ const unknownKey = error.issues.find((one) => 'unrecognized_keys' === one.code);
77
+ if (unknownKey !== undefined && 'unrecognized_keys' === unknownKey.code) {
78
+ const named = unknownKey.keys.map((k) => `"${k}"`).join(', ');
79
+ return `${FlowParseNote.UNSUPPORTED_SHAPE} key ${named}`;
80
+ }
60
81
  const issue = error.issues[0];
61
82
  if (undefined === issue)
62
83
  return FlowParseNote.UNSUPPORTED_SHAPE;
@@ -102,8 +123,48 @@ export function coerceFlowFileExpects(raw) {
102
123
  }
103
124
  return { ok: true, value: next };
104
125
  }
126
+ /**
127
+ * The issues a union rejection buries.
128
+ *
129
+ * `expect` accepts a predicate OR a v1 expect, and zod reports a union failure as one
130
+ * `invalid_union` issue whose branch errors live inside `unionErrors`. The one that matters is the
131
+ * v1 branch's `unrecognized_keys` — the misspelled field. Without this the file was still correctly
132
+ * REFUSED and the message said only "malformed", which sends the author back to hunt a
133
+ * one-character typo we had already identified.
134
+ */
135
+ function flatten(issues) {
136
+ return issues.flatMap((issue) => 'invalid_union' === issue.code
137
+ ? [issue, ...flatten(issue.unionErrors.flatMap((e) => e.issues))]
138
+ : [issue]);
139
+ }
105
140
  export function describeFlowZodFailure(error) {
106
- const issue = error.issues[0];
141
+ const all = flatten(error.issues);
142
+ /*
143
+ * The most SPECIFIC complaint wins, wherever in the union it was reported.
144
+ *
145
+ * An unrecognized key names the typo. Failing that, an issue whose path reaches INTO an expect
146
+ * names the field that was the wrong type — `{ signal: { count: 1 } }`, a signal object with no
147
+ * name, is reported on `steps.0.expect.signal` and that path is the whole answer. The union's own
148
+ * top-level `invalid_union` says only "malformed", which is true and useless.
149
+ */
150
+ const named = (one) => {
151
+ // NOT the discriminator complaint. `invalid_union_discriminator` on `expect.kind` is the
152
+ // PREDICATE branch objecting that a v1 expect has no `kind` — which is true of every v1 expect
153
+ // ever written and tells the author nothing. The useful issue is the one from the branch they
154
+ // were actually writing.
155
+ if ('invalid_union_discriminator' === one.code)
156
+ return false;
157
+ return (('steps' === one.path[0] && 'expect' === one.path[2] && one.path.length > 3) ||
158
+ ('success' === one.path[0] && one.path.length > 1));
159
+ };
160
+ // A refinement carries its own reason (a session ref, named by field), and that reason IS the fix.
161
+ const refused = all.find((one) => 'custom' === one.code);
162
+ if (refused !== undefined && 'steps' === refused.path[0] && 'number' === typeof refused.path[1]) {
163
+ return `${FlowParseNote.UNSUPPORTED_SHAPE} at step ${String(refused.path[1])}: ${refused.message}`;
164
+ }
165
+ if (refused !== undefined)
166
+ return `${FlowParseNote.UNSUPPORTED_SHAPE}: ${refused.message}`;
167
+ const issue = all.find((one) => 'unrecognized_keys' === one.code) ?? all.find(named) ?? error.issues[0];
107
168
  if (undefined === issue)
108
169
  return FlowParseNote.MALFORMED;
109
170
  const path = issue.path;
@@ -139,6 +200,26 @@ export function parseFlowFileText(text) {
139
200
  catch {
140
201
  return { ok: false, code: FlowErrorCode.PARSE_FAILED, detail: FlowParseNote.NOT_JSON };
141
202
  }
203
+ /*
204
+ * Version BEFORE schema. When the format does not match, nothing else in the file can be trusted
205
+ * to mean what this reader thinks it means, so the first unrelated field that happens to fail
206
+ * would be noise about the wrong thing -- and `version` is a `z.literal`, so the schema's own
207
+ * complaint about it is indistinguishable from a typo.
208
+ *
209
+ * Only a NUMBER that is not ours counts. A missing or non-numeric `version` is a damaged field
210
+ * rather than a different format, and stays PARSE_FAILED.
211
+ */
212
+ const declared = isRecord(parsed) ? parsed['version'] : undefined;
213
+ if ('number' === typeof declared && !READABLE_FLOW_VERSIONS.has(declared)) {
214
+ return {
215
+ ok: false,
216
+ code: FlowErrorCode.WRONG_VERSION,
217
+ detail: `this flow file is version ${String(declared)} and this Reticle reads versions ` +
218
+ `${[...READABLE_FLOW_VERSIONS].join(' and ')}. The file is not damaged, the reader is the ` +
219
+ 'wrong one. ' +
220
+ 'Upgrade or downgrade Reticle rather than editing the flow.',
221
+ };
222
+ }
142
223
  const coerced = coerceFlowFileExpects(parsed);
143
224
  if (!coerced.ok) {
144
225
  return { ok: false, code: FlowErrorCode.PARSE_FAILED, detail: coerced.detail };
@@ -6,7 +6,7 @@ import { classifyFlowAssertions } from './flow-classify.js';
6
6
  *
7
7
  * A replay used to report only in the language of the DOM: step 3 drifted, this assertion failed.
8
8
  * True, actionable, and illegible — the reader still had to reconstruct what stopped working. The
9
- * ledger in `.reticle/intent.json` already holds the other half (prose statements, declared → bound
9
+ * ledger in `.reticle/intent/` already holds the other half (prose statements, declared → bound
10
10
  * → proved, and the verdict that discharged each one), so a flow does not need its own notion of
11
11
  * intent. It needs an id.
12
12
  *
@@ -1,5 +1,6 @@
1
1
  import type { FlowReplayResult } from '@reticlehq/core';
2
2
  import type { ToolDeps } from '../../surface/tools/tool-kit.js';
3
+ import { withLearnedSources } from './learned-sources.js';
3
4
  /**
4
5
  * Write back what a replay taught the flow, once the replay has finished writing its own.
5
6
  *
@@ -50,3 +51,5 @@ export declare function replayAndLearn(deps: ToolDeps,
50
51
  * caught it. `seed` and anything added later travel the same path, so the whole object goes.
51
52
  */
52
53
  args: Record<string, unknown>): Promise<FlowReplayResult>;
54
+ /** Write back the files a clean replay's anchors resolved to. Best-effort, as `persistLearning` is. */
55
+ export declare function persistSources(deps: ToolDeps, args: Record<string, unknown>, sources: Parameters<typeof withLearnedSources>[1]): Promise<void>;
@@ -1,6 +1,16 @@
1
1
  import { asString } from '@reticlehq/core';
2
2
  import { flowsForSession } from './flow-store-for-session.js';
3
3
  import { replayNamedFlow } from './flow-replay-run.js';
4
+ import { recordingSources, withLearnedSources } from './learned-sources.js';
5
+ import { ReplayStatus } from '@reticlehq/core';
6
+ function projectOf(deps, args) {
7
+ try {
8
+ return deps.sessions.resolve(asString(args['sessionId'])).projectId;
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
4
14
  /**
5
15
  * Write back what a replay taught the flow, once the replay has finished writing its own.
6
16
  *
@@ -26,13 +36,7 @@ export async function persistLearning(deps, args, result) {
26
36
  if (0 === name.length)
27
37
  return result;
28
38
  try {
29
- let projectId;
30
- try {
31
- projectId = deps.sessions.resolve(asString(args['sessionId'])).projectId;
32
- }
33
- catch {
34
- projectId = undefined;
35
- }
39
+ const projectId = projectOf(deps, args);
36
40
  await flowsForSession(deps, projectId).flows.recordLearned(name, learned, projectId);
37
41
  }
38
42
  catch {
@@ -72,5 +76,32 @@ export async function replayAndLearn(deps,
72
76
  * caught it. `seed` and anything added later travel the same path, so the whole object goes.
73
77
  */
74
78
  args) {
75
- return await persistLearning(deps, args, await replayNamedFlow(deps, args));
79
+ let recorded;
80
+ const result = await replayNamedFlow(deps, args, (session) => {
81
+ recorded = recordingSources(session);
82
+ return recorded.session;
83
+ });
84
+ // Only a clean run teaches a file: a drifted step may have resolved to the wrong element.
85
+ if (result.status === ReplayStatus.OK && recorded !== undefined && recorded.sources.size > 0) {
86
+ await persistSources(deps, args, recorded.sources);
87
+ }
88
+ return await persistLearning(deps, args, result);
89
+ }
90
+ /** Write back the files a clean replay's anchors resolved to. Best-effort, as `persistLearning` is. */
91
+ export async function persistSources(deps, args, sources) {
92
+ const name = asString(args['flowName']) ?? '';
93
+ if (0 === name.length)
94
+ return;
95
+ try {
96
+ const projectId = projectOf(deps, args);
97
+ const store = flowsForSession(deps, projectId).flows;
98
+ const current = await store.load(name, projectId);
99
+ // Read first so a flow that already names every file is never rewritten.
100
+ if (!current.ok || withLearnedSources(current.value, sources) === undefined)
101
+ return;
102
+ await store.recordSources(name, sources, projectId);
103
+ }
104
+ catch {
105
+ // Bookkeeping only.
106
+ }
76
107
  }
@@ -2,6 +2,7 @@ import { FlowErrorCode, type CommandResult, type FlowFile, type FlowReplayResult
2
2
  import type { ArrivalClock } from '../../surface/tools/act/navigation/navigate-arrival.js';
3
3
  import type { SessionManager } from '../../portal/session/session-manager.js';
4
4
  import type { Session } from '../../portal/session/session.js';
5
+ import { DocumentLostDuringReplay, type FlowReplaySession } from './flow-replay.js';
5
6
  import type { ToolDeps } from '../../surface/tools/tool-kit.js';
6
7
  export declare function latestRecordedFlow(events: ReticleEvent[]): {
7
8
  name: string;
@@ -24,21 +25,39 @@ interface StartPathSession {
24
25
  * navigation was refused or the SDK never reconnected in the window.
25
26
  */
26
27
  export declare function startPathMismatchHint(flow: FlowFile, session: StartPathSession): string | undefined;
28
+ /** What replay learned on its way to step 0. Both fields absent = the tab was left exactly as it was. */
29
+ export interface StartPathArrival {
30
+ /** The successor session replay must continue on. Absent when nothing was navigated, or arrival timed out. */
31
+ session?: Session;
32
+ /** Set when the RESET itself is why step 1 cannot start — the reason a step-1 red is not drift. */
33
+ resetCost?: string;
34
+ }
27
35
  /**
28
- * Replay's half of the FlowFile contract: navigate to the flow's `startPath` before step 1.
36
+ * Replay's half of the FlowFile contract: put the tab on the flow's `startPath` before step 1, and
37
+ * RELOAD it if it is already there.
38
+ *
39
+ * The reload is the state contract. Replaying "add a task" twice against a tab nobody reset gives
40
+ * the second run a world the recording never saw, and its assertions then describe the first run
41
+ * rather than the app. Our own `replay-determinism.mjs` has always refreshed before every run — the
42
+ * determinism we measured was never the determinism a user got.
43
+ *
44
+ * `requires` is the declared opt-out, and its first caller. A flow that says it starts from state
45
+ * another flow established is the one flow a page load would destroy, so it is never reset — here
46
+ * or from the wrong page. Silence is not an opt-out: a flow that depends on leftovers without
47
+ * saying so is a flow whose green means nothing on a fresh machine, and CI is a fresh machine.
29
48
  *
30
49
  * A full-page load tears down the session socket, so the navigation must happen here — before any
31
50
  * step runs — and the replay continues on the session the SDK reconnects as (found via the same
32
51
  * tombstone rebind that lets `resolve(oldId)` answer after any navigation). Best-effort by design:
33
- * when the flow carries no startPath, the tab is already there, the current route is unobservable,
34
- * the navigation is refused, or the SDK never reconnects in the window, it returns undefined and
35
- * replay proceeds on the connected session as before — with startPathMismatchHint turning any
36
- * resulting drift into an actionable next move rather than a mystifying one.
52
+ * when the flow carries no startPath, the current route is unobservable, the navigation is refused,
53
+ * or the SDK never reconnects in the window, replay proceeds on the connected session as before —
54
+ * with startPathMismatchHint turning any resulting drift into an actionable next move rather than a
55
+ * mystifying one.
37
56
  */
38
57
  export declare function arriveAtStartPath(sessions: SessionManager, session: StartPathSession & {
39
58
  id: string;
40
59
  command(name: string, args?: Record<string, unknown>): Promise<CommandResult>;
41
- }, flow: FlowFile, timeoutMs?: number, clock?: ArrivalClock): Promise<Session | undefined>;
60
+ }, flow: FlowFile, timeoutMs?: number, clock?: ArrivalClock): Promise<StartPathArrival>;
42
61
  /**
43
62
  * Send a tab somewhere and wait for the session that comes back.
44
63
  *
@@ -56,13 +75,39 @@ export declare function navigateAndAwait(sessions: SessionManager, session: {
56
75
  /** Optional because a session's URL is, and `carryReticleIdentity` already handles its absence. */
57
76
  url?: string;
58
77
  command(name: string, args?: Record<string, unknown>): Promise<CommandResult>;
59
- }, destination: string, expectedPath: string, timeoutMs?: number, clock?: ArrivalClock): Promise<Session | undefined>;
60
- export declare function replayNamedFlow(deps: ToolDeps, args: Record<string, unknown>): Promise<FlowReplayResult>;
78
+ }, destination: string, expectedPath: string, timeoutMs?: number, clock?: ArrivalClock,
79
+ /** True when the destination is the page we are already on — see `arrivedSuccessor`. */
80
+ isReload?: boolean): Promise<Session | undefined>;
81
+ /**
82
+ * The honest answer when the document replay was driving went away mid-run.
83
+ *
84
+ * Not a pass: nothing graded the journey, and the steps in hand are only the ones the departed
85
+ * document answered. Not a failure either — the app was never observed doing anything wrong, and
86
+ * reporting one would be the false red that sends a reader into product code that is fine. So it
87
+ * lands in the bucket this engine already has for "nothing was proved", the same one an unmet
88
+ * precondition uses, with the steps that DID answer attached and no row invented for the one that
89
+ * did not.
90
+ *
91
+ * Why replay reports rather than follows: the successor cannot be identified at the moment the
92
+ * socket closes. `SessionManager.remove` runs from the close handler, writes the tombstone a
93
+ * successor is later matched against, and has nothing to match yet — so the rejection is a plain
94
+ * transport failure by design, and a step in the middle of a journey has no budget to sit and poll
95
+ * with. `navigateAndAwait` CAN wait, because it knows it asked for the navigation, and it does.
96
+ */
97
+ export declare function lostDocumentResult(name: string, lost: DocumentLostDuringReplay): FlowReplayResult;
98
+ export declare function replayNamedFlow(deps: ToolDeps, args: Record<string, unknown>,
99
+ /** Wraps the session the steps run on, so a caller can watch what they resolved. */
100
+ observe?: (session: FlowReplaySession) => FlowReplaySession): Promise<FlowReplayResult>;
61
101
  export declare function crossStepOnly(whole: readonly Contradiction[], steps: readonly FlowStepResult[]): Contradiction[];
62
102
  /**
63
103
  * The connecting session's project, or undefined when no browser is attached. Flow tools use it to
64
104
  * scope storage to the current app on a shared daemon; resolving must NOT throw here (list/load are
65
105
  * documented to work headless), so a missing/unknown session degrades to the global/legacy store.
106
+ *
107
+ * Re-exported, not re-implemented. This file carried a BYTE-IDENTICAL second copy, and the two
108
+ * drifted the moment `ProjectId` was threaded: `session-root`'s copy returned the brand while this
109
+ * one returned `string`, so every flow tool importing from here was handed a widened value and lost
110
+ * the brand one line after it was minted. Exactly the weak-annotation failure the brand's own doc
111
+ * records against `RunStore.list()`.
66
112
  */
67
- export declare function sessionProjectId(deps: ToolDeps, sessionId: string | undefined): string | undefined;
68
- export {};
113
+ export { sessionProjectId } from '../../memory/project/session-root.js';