@reticlehq/server 3.7.0 → 3.8.1

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 (578) hide show
  1. package/SKILL.md +59 -68
  2. package/bin/reticle.js +9 -2
  3. package/dist/command/cli/adhoc-suite.js +1 -1
  4. package/dist/command/cli/adhoc-verdict.d.ts +12 -0
  5. package/dist/command/cli/adhoc-verdict.js +62 -4
  6. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  7. package/dist/command/cli/answers/gate-hook-message.js +5 -1
  8. package/dist/command/cli/auth/cloud-session.d.ts +11 -19
  9. package/dist/command/cli/auth/cloud-session.js +23 -0
  10. package/dist/command/cli/auth/device-flow.d.ts +3 -37
  11. package/dist/command/cli/cli-doctor.js +66 -21
  12. package/dist/command/cli/cli-flow-commands.js +53 -11
  13. package/dist/command/cli/cli-kill.d.ts +8 -0
  14. package/dist/command/cli/cli-kill.js +43 -3
  15. package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
  16. package/dist/command/cli/cli-parse-feedback.js +13 -1
  17. package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
  18. package/dist/command/cli/cli-parse-grammar.js +1 -6
  19. package/dist/command/cli/cli-parse-serve.d.ts +24 -0
  20. package/dist/command/cli/cli-parse-serve.js +124 -0
  21. package/dist/command/cli/cli-parse.d.ts +12 -11
  22. package/dist/command/cli/cli-parse.js +100 -123
  23. package/dist/command/cli/cli-usage.d.ts +0 -11
  24. package/dist/command/cli/cli-usage.js +43 -16
  25. package/dist/command/cli/cli-verify.d.ts +1 -1
  26. package/dist/command/cli/cli-verify.js +29 -14
  27. package/dist/command/cli/cloud-cli.js +27 -9
  28. package/dist/command/cli/cloud-kit.js +3 -3
  29. package/dist/command/cli/cloud-link.d.ts +1 -20
  30. package/dist/command/cli/cloud-link.js +2 -0
  31. package/dist/command/cli/cloud-login.d.ts +6 -2
  32. package/dist/command/cli/cloud-login.js +13 -41
  33. package/dist/command/cli/config/checkout.d.ts +10 -0
  34. package/dist/command/cli/config/checkout.js +78 -0
  35. package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
  36. package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
  37. package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
  38. package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
  39. package/dist/command/cli/doctor/doctor-rows.js +4 -0
  40. package/dist/command/cli/drive/drive-attach.d.ts +5 -0
  41. package/dist/command/cli/drive/drive-attach.js +21 -1
  42. package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
  43. package/dist/command/cli/hook/codex-transcript.js +224 -0
  44. package/dist/command/cli/hook/episode-structure.d.ts +24 -0
  45. package/dist/command/cli/hook/episode-structure.js +138 -0
  46. package/dist/command/cli/hook/hook-command.d.ts +9 -0
  47. package/dist/command/cli/hook/hook-command.js +211 -0
  48. package/dist/command/cli/hook/hook-fast.d.ts +31 -0
  49. package/dist/command/cli/hook/hook-fast.js +136 -0
  50. package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
  51. package/dist/command/cli/hook/prompt-hook.js +199 -0
  52. package/dist/command/cli/hook/tool-hook.d.ts +21 -0
  53. package/dist/command/cli/hook/tool-hook.js +259 -0
  54. package/dist/command/cli/launch/cli-launch.d.ts +2 -0
  55. package/dist/command/cli/launch/cli-launch.js +22 -2
  56. package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
  57. package/dist/command/cli/mcp-command.d.ts +9 -0
  58. package/dist/command/cli/mcp-command.js +127 -41
  59. package/dist/command/cli/open-navigate.d.ts +43 -0
  60. package/dist/command/cli/open-navigate.js +85 -0
  61. package/dist/command/cli/ports/port-holder.d.ts +16 -4
  62. package/dist/command/cli/ports/port-holder.js +58 -12
  63. package/dist/command/cli/ports/resolve/cli-port.js +18 -2
  64. package/dist/command/cli/project-name.d.ts +1 -7
  65. package/dist/command/cli/project-name.js +1 -0
  66. package/dist/command/cli/report-command.d.ts +8 -2
  67. package/dist/command/cli/report-command.js +95 -18
  68. package/dist/command/cli/status-fields.d.ts +9 -2
  69. package/dist/command/cli/status-fields.js +148 -4
  70. package/dist/command/cli/try-command.d.ts +3 -4
  71. package/dist/command/cli/try-command.js +6 -11
  72. package/dist/command/cli/verify-results.d.ts +4 -1
  73. package/dist/command/cli/verify-results.js +3 -1
  74. package/dist/command/cli.d.ts +1 -7
  75. package/dist/command/cli.js +79 -160
  76. package/dist/command/daemon/daemon-resilience.d.ts +6 -1
  77. package/dist/command/daemon/daemon-resilience.js +31 -18
  78. package/dist/command/daemon/daemon.d.ts +11 -0
  79. package/dist/command/daemon/daemon.js +70 -2
  80. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
  81. package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
  82. package/dist/command/daemon/wake-decision.js +4 -2
  83. package/dist/command/daemon/win32-escape.d.ts +32 -0
  84. package/dist/command/daemon/win32-escape.js +119 -0
  85. package/dist/command/setup/agent-configs.d.ts +9 -0
  86. package/dist/command/setup/agent-configs.js +9 -0
  87. package/dist/command/setup/auto-approve.d.ts +10 -3
  88. package/dist/command/setup/auto-approve.js +51 -3
  89. package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
  90. package/dist/command/setup/bringup/dev-server-wait.js +5 -1
  91. package/dist/command/setup/bringup/ensure-daemon.js +4 -7
  92. package/dist/command/setup/init/init-host.js +6 -0
  93. package/dist/command/setup/init/init-port.js +5 -4
  94. package/dist/command/setup/init/init-runtime.js +7 -1
  95. package/dist/command/setup/node-effects.d.ts +8 -0
  96. package/dist/command/setup/node-effects.js +36 -4
  97. package/dist/command/setup/probe/dev-port.d.ts +26 -0
  98. package/dist/command/setup/probe/dev-port.js +117 -0
  99. package/dist/command/setup/probe/page-probe.d.ts +1 -1
  100. package/dist/command/setup/probe/page-probe.js +11 -3
  101. package/dist/command/setup/remaining-steps.d.ts +0 -7
  102. package/dist/command/setup/remaining-steps.js +3 -2
  103. package/dist/command/setup/run-setup.d.ts +11 -0
  104. package/dist/command/setup/run-setup.js +33 -5
  105. package/dist/command/setup/setup-command.d.ts +2 -0
  106. package/dist/command/setup/setup-command.js +22 -4
  107. package/dist/command/update/updater.d.ts +1 -6
  108. package/dist/command/update/updater.js +4 -5
  109. package/dist/command/update/version-seen.d.ts +24 -0
  110. package/dist/command/update/version-seen.js +65 -0
  111. package/dist/command/version/peer-announce.d.ts +9 -1
  112. package/dist/command/version/peer-announce.js +25 -2
  113. package/dist/command/version/sdk-fix.d.ts +5 -6
  114. package/dist/command/version/sdk-fix.js +17 -32
  115. package/dist/command/version/version-skew.d.ts +16 -1
  116. package/dist/command/version/version-skew.js +84 -10
  117. package/dist/features/crawl/crawl-tools.js +2 -2
  118. package/dist/features/crawl/crawl.js +2 -0
  119. package/dist/features/exhaust/ledger.d.ts +15 -96
  120. package/dist/features/exhaust/ledger.js +3 -3
  121. package/dist/features/harness/drive-runs.d.ts +28 -4
  122. package/dist/features/harness/drive-runs.js +99 -7
  123. package/dist/features/harness/drivers.d.ts +1 -1
  124. package/dist/features/harness/drivers.js +1 -1
  125. package/dist/features/harness/harness.d.ts +5 -0
  126. package/dist/features/harness/harness.js +24 -12
  127. package/dist/features/harness/platform/platform-drives.d.ts +7 -1
  128. package/dist/features/harness/platform/platform-drives.js +12 -7
  129. package/dist/features/harness/platform/refusal.d.ts +1 -1
  130. package/dist/features/harness/platform/refusal.js +4 -4
  131. package/dist/features/harness/platform/remote-drive.js +70 -28
  132. package/dist/features/harness/platform/server-driver.js +22 -1
  133. package/dist/features/harness/platform-config.d.ts +1 -10
  134. package/dist/features/harness/platform-config.js +0 -20
  135. package/dist/features/license/license.d.ts +2 -14
  136. package/dist/features/visual/step-shots.d.ts +33 -1
  137. package/dist/features/visual/step-shots.js +155 -1
  138. package/dist/features/visual/visual-tools.js +12 -4
  139. package/dist/hooks/coding-agents.d.ts +6 -0
  140. package/dist/hooks/coding-agents.js +24 -3
  141. package/dist/hooks/episode-stamp.d.ts +32 -0
  142. package/dist/hooks/episode-stamp.js +61 -0
  143. package/dist/hooks/hook-commands.js +19 -9
  144. package/dist/index.d.ts +5 -1
  145. package/dist/index.js +66 -24
  146. package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
  147. package/dist/judgement/app-graph/journal-steps.js +92 -0
  148. package/dist/judgement/capsule/capsule-store.d.ts +2 -39
  149. package/dist/judgement/domain/domain-model.js +1 -10
  150. package/dist/judgement/domain/domain-tools.js +4 -2
  151. package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
  152. package/dist/judgement/runs/artifact/run-store.js +153 -28
  153. package/dist/judgement/runs/default-run-id.d.ts +3 -1
  154. package/dist/judgement/runs/default-run-id.js +15 -3
  155. package/dist/judgement/runs/drive-run.d.ts +3 -3
  156. package/dist/judgement/runs/drive-run.js +11 -7
  157. package/dist/judgement/runs/run-ledger.d.ts +21 -0
  158. package/dist/judgement/runs/run-ledger.js +143 -0
  159. package/dist/judgement/runs/runner-port.d.ts +1 -1
  160. package/dist/judgement/runs/runner-port.js +8 -2
  161. package/dist/judgement/runs/verification-sync.d.ts +4 -3
  162. package/dist/judgement/runs/verification-sync.js +27 -5
  163. package/dist/language/flows/affected-tools.js +2 -1
  164. package/dist/language/flows/annotate-notes/annotate.js +8 -3
  165. package/dist/language/flows/annotate-tools.js +1 -1
  166. package/dist/language/flows/change/affected.d.ts +8 -0
  167. package/dist/language/flows/change/affected.js +20 -7
  168. package/dist/language/flows/decision.js +19 -4
  169. package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
  170. package/dist/language/flows/fields/flow-secret-field.js +67 -5
  171. package/dist/language/flows/flow-anchor.d.ts +5 -0
  172. package/dist/language/flows/flow-anchor.js +4 -0
  173. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  174. package/dist/language/flows/flow-cross-step.js +9 -0
  175. package/dist/language/flows/flow-expect-grammar.js +15 -10
  176. package/dist/language/flows/flow-intent.js +6 -1
  177. package/dist/language/flows/flow-journey.d.ts +8 -0
  178. package/dist/language/flows/flow-journey.js +13 -0
  179. package/dist/language/flows/flow-lease.d.ts +27 -0
  180. package/dist/language/flows/flow-lease.js +59 -0
  181. package/dist/language/flows/flow-replay-run.d.ts +1 -87
  182. package/dist/language/flows/flow-replay-run.js +111 -310
  183. package/dist/language/flows/flow-replay.js +21 -19
  184. package/dist/language/flows/flow-replayable.d.ts +38 -0
  185. package/dist/language/flows/flow-replayable.js +179 -0
  186. package/dist/language/flows/flow-start-path.d.ts +108 -0
  187. package/dist/language/flows/flow-start-path.js +313 -0
  188. package/dist/language/flows/flow-step-runners.d.ts +23 -2
  189. package/dist/language/flows/flow-step-runners.js +191 -32
  190. package/dist/language/flows/flow-store-for-session.d.ts +8 -1
  191. package/dist/language/flows/flow-store-for-session.js +6 -0
  192. package/dist/language/flows/flow-tools.d.ts +2 -23
  193. package/dist/language/flows/flow-tools.js +63 -113
  194. package/dist/language/flows/flows.d.ts +4 -6
  195. package/dist/language/flows/flows.js +90 -15
  196. package/dist/language/flows/heal-run.d.ts +10 -4
  197. package/dist/language/flows/heal-run.js +26 -9
  198. package/dist/language/flows/recording/flake.d.ts +3 -27
  199. package/dist/language/flows/recording/flake.js +1 -1
  200. package/dist/language/flows/recording/recording-backtrack.js +1 -4
  201. package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
  202. package/dist/language/flows/recording/tape/recordings.js +9 -3
  203. package/dist/language/flows/replay.d.ts +25 -5
  204. package/dist/language/flows/replay.js +83 -16
  205. package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
  206. package/dist/language/flows/suite/coverage.d.ts +9 -0
  207. package/dist/language/flows/suite/coverage.js +12 -1
  208. package/dist/language/flows/suite-selection.d.ts +25 -0
  209. package/dist/language/flows/suite-selection.js +41 -9
  210. package/dist/machine/git-tracks.d.ts +6 -0
  211. package/dist/machine/git-tracks.js +36 -0
  212. package/dist/memory/cloud/background-sync.d.ts +25 -0
  213. package/dist/memory/cloud/background-sync.js +79 -0
  214. package/dist/memory/cloud/cloud-config.d.ts +12 -1
  215. package/dist/memory/cloud/cloud-config.js +39 -13
  216. package/dist/memory/cloud/cloud-sync.d.ts +3 -24
  217. package/dist/memory/cloud/cloud-sync.js +5 -2
  218. package/dist/memory/cloud/harness-config.d.ts +0 -7
  219. package/dist/memory/cloud/memory-scope.d.ts +19 -0
  220. package/dist/memory/cloud/memory-scope.js +48 -0
  221. package/dist/memory/cloud/sync-answer.d.ts +43 -0
  222. package/dist/memory/cloud/sync-answer.js +42 -0
  223. package/dist/memory/cloud/sync-cycle.d.ts +40 -10
  224. package/dist/memory/cloud/sync-cycle.js +97 -144
  225. package/dist/memory/cloud/sync-daemon.d.ts +50 -3
  226. package/dist/memory/cloud/sync-daemon.js +473 -83
  227. package/dist/memory/cloud/sync-describe.d.ts +28 -0
  228. package/dist/memory/cloud/sync-describe.js +92 -0
  229. package/dist/memory/cloud/sync-disk.d.ts +9 -1
  230. package/dist/memory/cloud/sync-disk.js +137 -33
  231. package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
  232. package/dist/memory/cloud/sync-flow-versions.js +42 -0
  233. package/dist/memory/impact/impact-store.js +32 -15
  234. package/dist/memory/intent/intent-shard.d.ts +41 -266
  235. package/dist/memory/intent/intent-tools.js +20 -61
  236. package/dist/memory/intent/record-request.d.ts +27 -0
  237. package/dist/memory/intent/record-request.js +88 -0
  238. package/dist/memory/intent/settled-words.js +9 -1
  239. package/dist/memory/journal/drive-flow.d.ts +3 -0
  240. package/dist/memory/journal/drive-flow.js +11 -41
  241. package/dist/memory/journal/envelope-store.js +1 -1
  242. package/dist/memory/journal/envelope.d.ts +6 -154
  243. package/dist/memory/journal/episode-log.d.ts +107 -0
  244. package/dist/memory/journal/episode-log.js +302 -0
  245. package/dist/memory/journal/journal-recorder.d.ts +7 -0
  246. package/dist/memory/journal/journal-recorder.js +22 -9
  247. package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
  248. package/dist/memory/journal/on-disk/ambient-file.js +1 -1
  249. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
  250. package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
  251. package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
  252. package/dist/memory/journal/self-observation.js +3 -1
  253. package/dist/memory/journal/session-end.js +23 -9
  254. package/dist/memory/journal/session-journal.js +39 -20
  255. package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
  256. package/dist/memory/project/artifact-root-resolver.js +68 -23
  257. package/dist/memory/project/artifact-root.d.ts +17 -3
  258. package/dist/memory/project/artifact-root.js +47 -9
  259. package/dist/memory/project/fs/fs-port.d.ts +5 -0
  260. package/dist/memory/project/fs/fs-port.js +13 -0
  261. package/dist/memory/project/learned-routes.d.ts +3 -4
  262. package/dist/memory/project/learned-routes.js +3 -11
  263. package/dist/memory/project/link-directory.js +5 -2
  264. package/dist/memory/project/root-claims.d.ts +3 -1
  265. package/dist/memory/project/root-claims.js +41 -7
  266. package/dist/memory/project/session-root.d.ts +1 -1
  267. package/dist/memory/project/session-root.js +1 -15
  268. package/dist/memory/project/sync-status.js +5 -6
  269. package/dist/memory/recall/memory-tools.js +2 -2
  270. package/dist/memory/recall/platform-memory.d.ts +3 -1
  271. package/dist/memory/recall/platform-memory.js +9 -1
  272. package/dist/memory/recall/project-memory.js +15 -19
  273. package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
  274. package/dist/portal/bridge/auth-failure-reason.js +19 -0
  275. package/dist/portal/bridge/bridge.d.ts +19 -4
  276. package/dist/portal/bridge/bridge.js +150 -177
  277. package/dist/portal/bridge/harness-request.d.ts +15 -0
  278. package/dist/portal/bridge/harness-request.js +19 -0
  279. package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
  280. package/dist/portal/bridge/origin/origin-follow.js +110 -0
  281. package/dist/portal/bridge/pairing-token.d.ts +6 -0
  282. package/dist/portal/bridge/pairing-token.js +47 -16
  283. package/dist/portal/bridge/protocol-skew.d.ts +27 -2
  284. package/dist/portal/bridge/protocol-skew.js +66 -2
  285. package/dist/portal/bridge/session-nonces.d.ts +46 -0
  286. package/dist/portal/bridge/session-nonces.js +72 -0
  287. package/dist/portal/input/viewport-tools.d.ts +31 -0
  288. package/dist/portal/input/viewport-tools.js +106 -15
  289. package/dist/portal/pool/bounded-close.d.ts +23 -0
  290. package/dist/portal/pool/bounded-close.js +38 -0
  291. package/dist/portal/pool/browser-pool.d.ts +44 -39
  292. package/dist/portal/pool/browser-pool.js +247 -95
  293. package/dist/portal/pool/context-permissions.d.ts +9 -1
  294. package/dist/portal/pool/context-permissions.js +26 -0
  295. package/dist/portal/pool/lease-emulation.d.ts +37 -0
  296. package/dist/portal/pool/lease-emulation.js +29 -0
  297. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  298. package/dist/portal/pool/playwright-launcher.js +74 -7
  299. package/dist/portal/pool/pool-contract.d.ts +23 -1
  300. package/dist/portal/pool/pool-lease.d.ts +128 -0
  301. package/dist/portal/pool/pool-lease.js +49 -0
  302. package/dist/portal/pool/zero-install.d.ts +8 -1
  303. package/dist/portal/pool/zero-install.js +24 -2
  304. package/dist/portal/production/production-activation.d.ts +136 -0
  305. package/dist/portal/production/production-activation.js +173 -0
  306. package/dist/portal/production/production-drive.d.ts +52 -0
  307. package/dist/portal/production/production-drive.js +301 -0
  308. package/dist/portal/production/production-hello.d.ts +23 -0
  309. package/dist/portal/production/production-hello.js +23 -0
  310. package/dist/portal/realm/web-realm.js +42 -15
  311. package/dist/portal/session/control-envelope.d.ts +1 -1
  312. package/dist/portal/session/dev-server/served-document.d.ts +0 -5
  313. package/dist/portal/session/dev-server/served-document.js +6 -8
  314. package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
  315. package/dist/portal/session/first-run-wiring.d.ts +3 -36
  316. package/dist/portal/session/first-run-wiring.js +10 -103
  317. package/dist/portal/session/live-control-tools.js +61 -19
  318. package/dist/portal/session/no-session-next-action.d.ts +11 -0
  319. package/dist/portal/session/no-session-next-action.js +40 -15
  320. package/dist/portal/session/no-session-watch.d.ts +7 -0
  321. package/dist/portal/session/no-session-watch.js +67 -23
  322. package/dist/portal/session/output-budget.d.ts +10 -0
  323. package/dist/portal/session/output-budget.js +16 -0
  324. package/dist/portal/session/presence/session-lease.d.ts +2 -0
  325. package/dist/portal/session/presence/session-lease.js +8 -0
  326. package/dist/portal/session/recorded-gaps.d.ts +15 -11
  327. package/dist/portal/session/recorded-gaps.js +39 -18
  328. package/dist/portal/session/serving-directory.d.ts +24 -2
  329. package/dist/portal/session/serving-directory.js +93 -5
  330. package/dist/portal/session/session-info.d.ts +26 -0
  331. package/dist/portal/session/session-info.js +20 -0
  332. package/dist/portal/session/session-manager.d.ts +35 -3
  333. package/dist/portal/session/session-manager.js +69 -14
  334. package/dist/portal/session/session-successor.js +1 -9
  335. package/dist/portal/session/session-tools.js +22 -4
  336. package/dist/portal/session/session.d.ts +10 -10
  337. package/dist/portal/session/session.js +37 -29
  338. package/dist/portal/session/zero-install-first-call.d.ts +7 -2
  339. package/dist/portal/session/zero-install-first-call.js +43 -20
  340. package/dist/start-wiring.d.ts +2 -2
  341. package/dist/start-wiring.js +17 -3
  342. package/dist/status-payload.d.ts +11 -1
  343. package/dist/status-payload.js +7 -1
  344. package/dist/surface/agent-presence-grace.d.ts +11 -0
  345. package/dist/surface/agent-presence-grace.js +34 -0
  346. package/dist/surface/http-server.d.ts +12 -1
  347. package/dist/surface/http-server.js +64 -15
  348. package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
  349. package/dist/surface/mcp/faults/mcp-outage.js +19 -1
  350. package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
  351. package/dist/surface/mcp/mcp-proxy.js +194 -267
  352. package/dist/surface/mcp/mcp.d.ts +20 -3
  353. package/dist/surface/mcp/mcp.js +88 -25
  354. package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
  355. package/dist/surface/mcp/proxy/drive-calls.js +114 -5
  356. package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
  357. package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
  358. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
  359. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
  360. package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
  361. package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
  362. package/dist/surface/mcp/server-instructions.js +35 -16
  363. package/dist/surface/mcp/shared-params.d.ts +8 -0
  364. package/dist/surface/mcp/shared-params.js +15 -1
  365. package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
  366. package/dist/surface/mcp/sse-frame-parser.js +18 -0
  367. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  368. package/dist/surface/mcp/surface-vocabulary.js +13 -0
  369. package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
  370. package/dist/surface/mcp/tool-catalog-cache.js +14 -4
  371. package/dist/surface/tools/account-tool.d.ts +4 -3
  372. package/dist/surface/tools/account-tool.js +20 -31
  373. package/dist/surface/tools/act/act-danger.d.ts +2 -0
  374. package/dist/surface/tools/act/act-danger.js +41 -14
  375. package/dist/surface/tools/act/act-observation.d.ts +11 -0
  376. package/dist/surface/tools/act/act-observation.js +33 -0
  377. package/dist/surface/tools/act/act-preflight.js +10 -3
  378. package/dist/surface/tools/act/act-sequence-retry.js +2 -1
  379. package/dist/surface/tools/act/act-target.js +19 -5
  380. package/dist/surface/tools/act/durable.js +1 -1
  381. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
  382. package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
  383. package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
  384. package/dist/surface/tools/act/unsettled-find.js +71 -0
  385. package/dist/surface/tools/act-sequence-tool.js +53 -24
  386. package/dist/surface/tools/act-tools.d.ts +2 -3
  387. package/dist/surface/tools/act-tools.js +49 -47
  388. package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
  389. package/dist/surface/tools/agent/for-the-agent.js +41 -3
  390. package/dist/surface/tools/assert/assert-grade.js +8 -3
  391. package/dist/surface/tools/assert/assert-verdict.js +4 -2
  392. package/dist/surface/tools/browser-tools.js +7 -4
  393. package/dist/surface/tools/chat-drives.js +50 -25
  394. package/dist/surface/tools/coverage-tools.js +1 -1
  395. package/dist/surface/tools/dynamic-tools.d.ts +8 -0
  396. package/dist/surface/tools/dynamic-tools.js +89 -34
  397. package/dist/surface/tools/empty-session-list.d.ts +18 -25
  398. package/dist/surface/tools/empty-session-list.js +67 -38
  399. package/dist/surface/tools/error-recovery.d.ts +17 -7
  400. package/dist/surface/tools/error-recovery.js +53 -20
  401. package/dist/surface/tools/explore-tools.d.ts +10 -0
  402. package/dist/surface/tools/explore-tools.js +72 -19
  403. package/dist/surface/tools/feedback-tools.js +23 -2
  404. package/dist/surface/tools/gap-novelty.d.ts +6 -0
  405. package/dist/surface/tools/gap-novelty.js +9 -0
  406. package/dist/surface/tools/gaps/capability-absences.js +4 -3
  407. package/dist/surface/tools/harness-explore.d.ts +1 -1
  408. package/dist/surface/tools/harness-explore.js +6 -4
  409. package/dist/surface/tools/harness-gate.d.ts +43 -21
  410. package/dist/surface/tools/harness-gate.js +55 -81
  411. package/dist/surface/tools/harness-script.d.ts +2 -0
  412. package/dist/surface/tools/harness-script.js +11 -6
  413. package/dist/surface/tools/harness-toolset.js +3 -4
  414. package/dist/surface/tools/invoke-tool.d.ts +0 -8
  415. package/dist/surface/tools/invoke-tool.js +164 -178
  416. package/dist/surface/tools/lease-hint.d.ts +9 -0
  417. package/dist/surface/tools/lease-hint.js +22 -3
  418. package/dist/surface/tools/lease-permissions.d.ts +1 -1
  419. package/dist/surface/tools/lease-readiness.d.ts +55 -0
  420. package/dist/surface/tools/lease-readiness.js +150 -2
  421. package/dist/surface/tools/lease-tools.d.ts +3 -75
  422. package/dist/surface/tools/lease-tools.js +124 -188
  423. package/dist/surface/tools/map-tools.d.ts +20 -0
  424. package/dist/surface/tools/map-tools.js +145 -0
  425. package/dist/surface/tools/merge-tools.js +11 -7
  426. package/dist/surface/tools/next-step.js +6 -3
  427. package/dist/surface/tools/observe-tools.js +46 -35
  428. package/dist/surface/tools/platform-moment.d.ts +3 -12
  429. package/dist/surface/tools/platform-moment.js +12 -24
  430. package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
  431. package/dist/surface/tools/read/complete-snapshot.js +13 -2
  432. package/dist/surface/tools/read/query-paginate.js +6 -0
  433. package/dist/surface/tools/read/query-shape.d.ts +9 -0
  434. package/dist/surface/tools/read/query-shape.js +15 -0
  435. package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
  436. package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
  437. package/dist/surface/tools/read-tools.js +5 -5
  438. package/dist/surface/tools/real-input-attempt.d.ts +11 -0
  439. package/dist/surface/tools/real-input-attempt.js +206 -38
  440. package/dist/surface/tools/reconcile-tools.js +21 -8
  441. package/dist/surface/tools/reticle-result.d.ts +3 -0
  442. package/dist/surface/tools/reticle-result.js +8 -1
  443. package/dist/surface/tools/run-report.d.ts +22 -0
  444. package/dist/surface/tools/run-report.js +141 -0
  445. package/dist/surface/tools/session-ports.d.ts +57 -0
  446. package/dist/surface/tools/session-ports.js +102 -0
  447. package/dist/surface/tools/tool-kit.d.ts +58 -4
  448. package/dist/surface/tools/tool-kit.js +140 -6
  449. package/dist/surface/tools/tool-surface.d.ts +2 -0
  450. package/dist/surface/tools/tool-surface.js +45 -0
  451. package/dist/surface/tools/tools.js +49 -87
  452. package/dist/telemetry/automation-hint.d.ts +5 -0
  453. package/dist/telemetry/automation-hint.js +7 -0
  454. package/dist/telemetry/cli-telemetry.d.ts +22 -0
  455. package/dist/telemetry/cli-telemetry.js +80 -11
  456. package/dist/telemetry/daemon-telemetry.d.ts +3 -1
  457. package/dist/telemetry/daemon-telemetry.js +9 -2
  458. package/dist/telemetry/feedback-cli.d.ts +1 -1
  459. package/dist/telemetry/feedback-cli.js +8 -3
  460. package/dist/telemetry/feedback.d.ts +1 -1
  461. package/dist/telemetry/feedback.js +51 -4
  462. package/dist/telemetry/harness-run.d.ts +3 -3
  463. package/dist/telemetry/harness-run.js +18 -7
  464. package/dist/telemetry/hud-events.d.ts +1 -1
  465. package/dist/telemetry/hud-metrics.d.ts +6 -3
  466. package/dist/telemetry/hud-metrics.js +20 -8
  467. package/dist/telemetry/install-source.js +1 -1
  468. package/dist/telemetry/mcp-connection.d.ts +1 -1
  469. package/dist/telemetry/mcp-connection.js +21 -2
  470. package/dist/telemetry/onboarding-firsts.d.ts +6 -1
  471. package/dist/telemetry/onboarding-firsts.js +41 -1
  472. package/dist/telemetry/proxy-telemetry.d.ts +42 -0
  473. package/dist/telemetry/proxy-telemetry.js +70 -1
  474. package/dist/telemetry/session-metrics.d.ts +1 -3
  475. package/dist/telemetry/session-metrics.js +16 -12
  476. package/dist/telemetry/telemetry.d.ts +9 -1
  477. package/dist/telemetry/telemetry.js +16 -1
  478. package/dist/telemetry/verification-of.d.ts +3 -1
  479. package/dist/telemetry/verification-of.js +7 -1
  480. package/dist/wire-journal.js +12 -2
  481. package/docs/README.md +4 -2
  482. package/docs/actions.mdx +7 -6
  483. package/docs/adapters.md +2 -1
  484. package/docs/agent-cheatsheet.md +19 -5
  485. package/docs/architecture.md +13 -10
  486. package/docs/autodrive.md +6 -5
  487. package/docs/benchmarks.md +2 -2
  488. package/docs/capabilities.mdx +1 -1
  489. package/docs/cli/affected.mdx +1 -1
  490. package/docs/cli/cloud.mdx +6 -6
  491. package/docs/cli/doctor.mdx +3 -1
  492. package/docs/cli/feedback.mdx +11 -7
  493. package/docs/cli/gate.mdx +1 -1
  494. package/docs/cli/help.mdx +1 -1
  495. package/docs/cli/init.mdx +20 -16
  496. package/docs/cli/mcp.mdx +5 -2
  497. package/docs/cli/open.mdx +5 -2
  498. package/docs/cli/report.mdx +1 -1
  499. package/docs/cli/restart.mdx +1 -1
  500. package/docs/cli/rollback.mdx +4 -4
  501. package/docs/cli/serve.mdx +7 -5
  502. package/docs/cli/setup.mdx +1 -1
  503. package/docs/cli/status.mdx +6 -2
  504. package/docs/cli/stop.mdx +3 -3
  505. package/docs/cli/telemetry.mdx +5 -1
  506. package/docs/cli/try.mdx +8 -8
  507. package/docs/cli/update.mdx +8 -5
  508. package/docs/cli/verify.mdx +12 -5
  509. package/docs/cli/version.mdx +1 -1
  510. package/docs/cli.mdx +10 -6
  511. package/docs/debugging.md +1 -1
  512. package/docs/deploy-checks.md +8 -6
  513. package/docs/desktop.mdx +14 -18
  514. package/docs/enterprise.md +6 -6
  515. package/docs/environment.mdx +5 -3
  516. package/docs/faq.mdx +9 -6
  517. package/docs/first-drive.md +19 -9
  518. package/docs/flows.md +3 -4
  519. package/docs/for-agents.md +1 -1
  520. package/docs/frameworks.mdx +34 -11
  521. package/docs/getting-started.md +46 -29
  522. package/docs/http-transport.md +2 -2
  523. package/docs/human-control.md +8 -8
  524. package/docs/index.mdx +2 -2
  525. package/docs/install-agentic.mdx +18 -19
  526. package/docs/install-manual.mdx +8 -4
  527. package/docs/integration-patterns.md +5 -5
  528. package/docs/multi-agent-testing.md +7 -5
  529. package/docs/packages/browser.mdx +34 -2
  530. package/docs/packages/core.mdx +2 -1
  531. package/docs/packages/electron.mdx +15 -3
  532. package/docs/packages/eslint-plugin.mdx +1 -1
  533. package/docs/packages/next.mdx +17 -5
  534. package/docs/packages/react.mdx +8 -1
  535. package/docs/packages/server.mdx +3 -3
  536. package/docs/packages/tauri.mdx +3 -3
  537. package/docs/packages/vite-plugin.mdx +1 -1
  538. package/docs/packages.mdx +19 -6
  539. package/docs/platform-integration.md +12 -4
  540. package/docs/predicates.mdx +18 -7
  541. package/docs/production-mode.md +108 -0
  542. package/docs/prompt-capture.md +99 -0
  543. package/docs/quickstart.mdx +14 -12
  544. package/docs/recipes.mdx +9 -8
  545. package/docs/required-checks.md +3 -2
  546. package/docs/skill-file.mdx +4 -3
  547. package/docs/skill-setup.md +2 -2
  548. package/docs/skill.md +62 -70
  549. package/docs/state-files.md +1 -0
  550. package/docs/state-management.mdx +11 -9
  551. package/docs/system-map.md +14 -13
  552. package/docs/telemetry.md +32 -24
  553. package/docs/testing.md +9 -5
  554. package/docs/tools/act-and-wait.mdx +17 -13
  555. package/docs/tools/act-sequence.mdx +40 -66
  556. package/docs/tools/act.mdx +7 -5
  557. package/docs/tools/assert.mdx +1 -1
  558. package/docs/tools/look.mdx +1 -1
  559. package/docs/tools/navigate.mdx +1 -1
  560. package/docs/tools/network.mdx +13 -11
  561. package/docs/tools/overview.mdx +3 -3
  562. package/docs/tools/query.mdx +3 -3
  563. package/docs/tools/session-and-feedback.mdx +9 -6
  564. package/docs/tools/sessions.mdx +7 -0
  565. package/docs/tools/snapshot.mdx +9 -7
  566. package/docs/tools/tools-and-run.mdx +11 -10
  567. package/docs/tools/verify.mdx +1 -1
  568. package/docs/troubleshooting.mdx +21 -9
  569. package/docs/usage.md +58 -43
  570. package/docs/use-cases.mdx +2 -2
  571. package/docs/vs-chrome-devtools-mcp.mdx +5 -5
  572. package/docs/vs-playwright-mcp.mdx +5 -4
  573. package/docs/what-is-recorded.md +12 -7
  574. package/docs/what-is-reticle.mdx +2 -2
  575. package/docs/why-reticle.mdx +1 -1
  576. package/package.json +9 -12
  577. package/dist/command/dev/stale-issue-guard.d.ts +0 -34
  578. package/dist/command/dev/stale-issue-guard.js +0 -69
@@ -33,6 +33,10 @@
33
33
  import { FlowErrorCode, ReticleDir, SYNC_BATCH_LIMITS, flowJsonReplacer } from '@reticlehq/core';
34
34
  import { hashPayload } from './sync-hash.js';
35
35
  import { syncScreenshots } from './sync-screenshots.js';
36
+ import { describeTransportError, problemsOf } from './sync-describe.js';
37
+ import { conflictOf, flowBases, versionsIn } from './sync-flow-versions.js';
38
+ import { asJson, flowName, isRecord, numberAt, numbersIn, partResult, } from './sync-answer.js';
39
+ export { FLOW_CONFLICT_CODE } from './sync-flow-versions.js';
36
40
  /** The server's own doors. Kept beside the code that calls them, like the other cloud paths. */
37
41
  const SYNC_PATH = '/v1/sync';
38
42
  const SYNC_STATUS_PATH = '/v1/sync/status';
@@ -57,49 +61,19 @@ export const DERIVED_RECORDS = [
57
61
  // The notes a human pinned in the HUD, pending and resolved: the review trail a team reads.
58
62
  { kind: 'notes', file: ReticleDir.NOTES_FILE },
59
63
  ];
60
- const asJson = (text) => {
61
- try {
62
- return JSON.parse(text);
63
- }
64
- catch {
65
- return undefined;
66
- }
67
- };
68
- const isRecord = (v) => 'object' === typeof v && null !== v;
69
- /** A list of numbers, or undefined when the server did not send a list at all. */
70
- const numbersIn = (v) => Array.isArray(v) ? v.filter((n) => 'number' === typeof n) : undefined;
71
- /** A numeric field of an artifact, or undefined when it has none. */
72
- const numberAt = (v, field) => {
73
- const n = isRecord(v) ? v[field] : undefined;
74
- return 'number' === typeof n ? n : undefined;
75
- };
76
- /** The name a person knows a flow by, falling back to its position. */
77
- const flowName = (flow, index) => {
78
- const name = isRecord(flow) ? flow['name'] : undefined;
79
- return 'string' === typeof name && name.length > 0 ? name : `flow #${String(index)}`;
80
- };
64
+ /** A refusal older than this is offered again, whatever else is unchanged. */
65
+ export const REFUSAL_RETRY_AFTER_MS = 7 * 24 * 60 * 60 * 1000;
66
+ /** Does an earlier refusal still hold? An entry from before the stamp existed is asked once more. */
67
+ function refusalHolds(prior, now) {
68
+ return (prior.acceptsHash === now.acceptsHash &&
69
+ prior.version === now.version &&
70
+ 'number' === typeof prior.at &&
71
+ now.at - prior.at < REFUSAL_RETRY_AFTER_MS);
72
+ }
81
73
  const FLOW_VERSION_FIX = 'update the platform or remove `compare` from the flow';
82
74
  const RUN_VERSION_FIX = 'update the platform';
83
- /** The server's per-part answer: accepted count and rejection list, each read defensively. */
84
75
  /** A 200 that never confirmed the runs it was sent. Kept queued, and said, rather than dropped. */
85
76
  export const MSG_RUNS_UNCONFIRMED = 'sync 200 without a runs result: the server did not confirm the runs it was sent, so they stay queued';
86
- function partResult(body, part) {
87
- const raw = isRecord(body[part]) ? body[part] : {};
88
- const accepted = 'number' === typeof raw['accepted'] ? raw['accepted'] : 0;
89
- const rejected = (Array.isArray(raw['rejected']) ? raw['rejected'] : [])
90
- .filter(isRecord)
91
- .map((r) => ({
92
- index: 'number' === typeof r['index'] ? r['index'] : -1,
93
- reason: 'string' === typeof r['reason'] ? r['reason'] : 'no reason given',
94
- code: r['code'],
95
- version: r['version'],
96
- }));
97
- return { accepted, rejected };
98
- }
99
- /**
100
- * Split runs into requests bounded by count and by serialized size. A run bigger than the byte
101
- * bound on its own still goes, alone: it cannot be split, and holding it back would hide it.
102
- */
103
77
  /** The delivery record, kept only for the runs still on disk — the same bound as `sentRunIds`. */
104
78
  function heldHashes(runs, recorded) {
105
79
  const out = {};
@@ -110,6 +84,10 @@ function heldHashes(runs, recorded) {
110
84
  }
111
85
  return out;
112
86
  }
87
+ /**
88
+ * Split runs into requests bounded by count and by serialized size. A run bigger than the byte
89
+ * bound on its own still goes, alone: it cannot be split, and holding it back would hide it.
90
+ */
113
91
  function batchRuns(runs) {
114
92
  const batches = [];
115
93
  let current = [];
@@ -129,10 +107,6 @@ function batchRuns(runs) {
129
107
  batches.push(current);
130
108
  return batches;
131
109
  }
132
- /**
133
- * Run one cycle. Never throws: the local record is already safe on disk, and a sync that can take
134
- * down the thing it is backing up is worse than no sync.
135
- */
136
110
  /** The command that moves a folder's earlier runs to the link it has now. */
137
111
  export const MOVE_HISTORY_COMMAND = 'reticle sync --move-history';
138
112
  /**
@@ -154,6 +128,10 @@ function stateForLink(deps) {
154
128
  }
155
129
  return { link, ...(true === deps.moveHistory ? {} : { linkedAt: deps.now() }) };
156
130
  }
131
+ /**
132
+ * Run one cycle. Never throws: the local record is already safe on disk, and a sync that can take
133
+ * down the thing it is backing up is worse than no sync.
134
+ */
157
135
  export async function runSyncCycle(given) {
158
136
  const deps = { ...given, state: stateForLink(given) };
159
137
  const empty = {
@@ -231,6 +209,11 @@ export async function runSyncCycle(given) {
231
209
  // 2. SEND — only what the server does not already have, and only what it can read.
232
210
  const allRuns = deps.source.runs();
233
211
  const acceptsHash = hashPayload(held.accepts ?? null);
212
+ const stamp = {
213
+ acceptsHash,
214
+ ...(deps.clientVersion === undefined ? {} : { version: deps.clientVersion }),
215
+ at: deps.now(),
216
+ };
234
217
  const priorRefusals = isRecord(deps.state.refusedRuns) ? deps.state.refusedRuns : {};
235
218
  const refusedRuns = {};
236
219
  const notRetried = [];
@@ -253,7 +236,7 @@ export async function runSyncCycle(given) {
253
236
  const prior = priorRefusals[run.runId];
254
237
  const unchanged = prior !== undefined &&
255
238
  'string' === typeof prior.reason &&
256
- prior.acceptsHash === acceptsHash &&
239
+ refusalHolds(prior, stamp) &&
257
240
  prior.payloadHash === hashPayload(run.payload);
258
241
  if (!unchanged)
259
242
  return true;
@@ -332,7 +315,19 @@ export async function runSyncCycle(given) {
332
315
  return false;
333
316
  });
334
317
  const capsulesAll = deps.source.capsules();
335
- const flowsHash = hashPayload(flows);
318
+ const named = flows.map((flow, index) => ({ flow, name: flowName(flow, index) }));
319
+ const priorFlowHashes = {};
320
+ if (isRecord(deps.state.sentFlowHashes)) {
321
+ for (const [name, hash] of Object.entries(deps.state.sentFlowHashes))
322
+ if ('string' === typeof hash)
323
+ priorFlowHashes[name] = hash;
324
+ }
325
+ else if (deps.state.sentFlowsHash === hashPayload(flows))
326
+ for (const { flow, name } of named)
327
+ priorFlowHashes[name] = hashPayload(flow);
328
+ // Only the flows that changed since the platform last took them; the platform upserts by name.
329
+ const pendingFlows = named.filter(({ flow, name }) => priorFlowHashes[name] !== hashPayload(flow));
330
+ const flowsHash = hashPayload(pendingFlows.map(({ flow }) => flow));
336
331
  const capsulesHash = hashPayload(capsulesAll);
337
332
  const priorSets = isRecord(deps.state.refusedSets) ? deps.state.refusedSets : {};
338
333
  const setsNotRetried = [];
@@ -343,7 +338,7 @@ export async function runSyncCycle(given) {
343
338
  return false;
344
339
  const prior = priorSets[part];
345
340
  if (prior !== undefined && prior.hash === hash) {
346
- if (prior.acceptsHash !== acceptsHash)
341
+ if (!refusalHolds(prior, stamp))
347
342
  return true;
348
343
  refusedSets[part] = prior;
349
344
  setsNotRetried.push({ part, count: prior.count, reason: prior.reason });
@@ -351,10 +346,26 @@ export async function runSyncCycle(given) {
351
346
  }
352
347
  return hash !== sentHash;
353
348
  };
354
- const flowsChanged = due('flow', flowsHash, flows.length, deps.state.sentFlowsHash);
349
+ const flowsChanged = due('flow', flowsHash, pendingFlows.length, undefined);
355
350
  const capsulesChanged = due('capsule', capsulesHash, capsulesAll.length, deps.state.sentCapsulesHash);
356
- if (flowsChanged)
357
- bundle['flows'] = flows;
351
+ // Each flow names the version it was based on; a stale base is answered with a conflict.
352
+ const priorVersions = versionsIn(deps.state.flowVersions);
353
+ const taken = {};
354
+ if (flowsChanged) {
355
+ bundle['flows'] = pendingFlows.map(({ flow }) => flow);
356
+ bundle['flowBases'] = flowBases(pendingFlows, priorVersions, priorFlowHashes);
357
+ }
358
+ const conflictCopies = [];
359
+ // What the platform holds from here once this cycle is answered: kept for the flows still here.
360
+ const flowHashes = {};
361
+ for (const { name } of named) {
362
+ const hash = priorFlowHashes[name];
363
+ if (hash !== undefined)
364
+ flowHashes[name] = hash;
365
+ const v = priorVersions[name];
366
+ if (v !== undefined)
367
+ taken[name] = v;
368
+ }
358
369
  const capsules = capsulesChanged ? capsulesAll : [];
359
370
  if (capsules.length > 0)
360
371
  bundle['capsules'] = capsules;
@@ -406,7 +417,7 @@ export async function runSyncCycle(given) {
406
417
  recorded[run.runId] = hashPayload(run.payload);
407
418
  }
408
419
  else
409
- refusedRuns[run.runId] = { reason, payloadHash: hashPayload(run.payload), acceptsHash };
420
+ refusedRuns[run.runId] = { reason, payloadHash: hashPayload(run.payload), ...stamp };
410
421
  });
411
422
  runsRejected.push(...runs.rejected.map((r) => ({ index: r.index + offset, reason: r.reason })));
412
423
  offset += batch.length;
@@ -418,8 +429,33 @@ export async function runSyncCycle(given) {
418
429
  const capsulesPart = partResult(answer, 'capsules');
419
430
  flowsSent = flowsPart.accepted;
420
431
  capsulesSent = capsulesPart.accepted;
432
+ const refusedFlows = new Set(flowsPart.rejected.map((r) => r.index));
433
+ const answered = isRecord(answer['flows']) ? answer['flows']['versions'] : undefined;
434
+ if (flowsChanged)
435
+ pendingFlows.forEach(({ flow, name }, index) => {
436
+ if (refusedFlows.has(index))
437
+ return;
438
+ flowHashes[name] = hashPayload(flow);
439
+ const v = isRecord(answered) ? answered[name] : undefined;
440
+ if ('string' === typeof v)
441
+ taken[name] = v;
442
+ });
443
+ // Another machine saved over the version this edit was based on: keep both, take theirs.
444
+ flowsPart.rejected = flowsPart.rejected.filter((r) => {
445
+ const name = pendingFlows[r.index]?.name;
446
+ const theirs = conflictOf(r);
447
+ if (name === undefined || theirs === undefined)
448
+ return true;
449
+ const copy = deps.sink.resolveFlowConflict?.(name, theirs.flow);
450
+ if (copy === undefined)
451
+ return true;
452
+ conflictCopies.push(copy);
453
+ flowHashes[name] = hashPayload(theirs.flow);
454
+ taken[name] = theirs.version;
455
+ return false;
456
+ });
421
457
  for (const r of flowsPart.rejected) {
422
- const name = flowName(flows[r.index], r.index);
458
+ const name = pendingFlows[r.index]?.name ?? flowName(undefined, r.index);
423
459
  refused.push({
424
460
  part: 'flow',
425
461
  reason: FlowErrorCode.WRONG_VERSION === r.code && 'number' === typeof r.version
@@ -433,14 +469,14 @@ export async function runSyncCycle(given) {
433
469
  if (flowsPart.rejected.length > 0)
434
470
  refusedSets.flow = {
435
471
  hash: flowsHash,
436
- acceptsHash,
472
+ ...stamp,
437
473
  count: flowsPart.rejected.length,
438
474
  reason: firstOf('flow'),
439
475
  };
440
476
  if (capsulesPart.rejected.length > 0)
441
477
  refusedSets.capsule = {
442
478
  hash: capsulesHash,
443
- acceptsHash,
479
+ ...stamp,
444
480
  count: capsulesPart.rejected.length,
445
481
  reason: firstOf('capsule'),
446
482
  };
@@ -474,12 +510,17 @@ export async function runSyncCycle(given) {
474
510
  }
475
511
  if (deps.projectId !== undefined)
476
512
  nextState.sentFlowNames = kept;
513
+ // Every cycle, so a flow gone from here is forgotten too: saved again, it goes again.
514
+ nextState.sentFlowHashes = flowHashes;
515
+ nextState.flowVersions = taken;
516
+ // Open until the person deletes the copy (or merges it back): counted on every cycle till then.
517
+ const flowConflicts = [...(deps.state.flowConflicts ?? []), ...conflictCopies].filter((copy, i, all) => all.indexOf(copy) === i && (onDisk.has(copy) || conflictCopies.includes(copy)));
518
+ nextState.flowConflicts = flowConflicts;
519
+ delete nextState.sentFlowsHash;
477
520
  if (requests > 0 && pushError === undefined) {
478
521
  nextState.lastPushAt = deps.now();
479
522
  // The platform answered for these sets, refusals included (reported above, and not re-offered
480
523
  // until the set changes — the same rule as a refused run).
481
- if (flowsChanged)
482
- nextState.sentFlowsHash = flowsHash;
483
524
  if (flowsChanged && deps.projectId !== undefined)
484
525
  nextState.sentFlowNames = [...new Set([...kept, ...flows.map(flowName)])];
485
526
  if (capsules.length > 0)
@@ -578,6 +619,7 @@ export async function runSyncCycle(given) {
578
619
  ...(0 === screenshotsSent ? {} : { screenshotsSent }),
579
620
  runsRejected,
580
621
  flowsSent,
622
+ ...(0 === flowConflicts.length ? {} : { flowConflicts }),
581
623
  capsulesSent,
582
624
  derivedSent,
583
625
  refused,
@@ -613,92 +655,3 @@ export async function runSyncCycle(given) {
613
655
  return { ...empty, error: message };
614
656
  }
615
657
  }
616
- /**
617
- * Say WHICH host failed and WHY.
618
- *
619
- * Node's fetch rejects with the bare string "fetch failed" and hides the real reason — a refused
620
- * connection, a bad hostname, an expired certificate — one level down in `cause`. A machine that
621
- * has quietly stopped syncing shows that string in `reticle whoami`, and on its own it is
622
- * indistinguishable from every other network problem, so the person reading it learns nothing and
623
- * checks nothing. Naming the origin and the underlying code turns it into one thing to look at.
624
- */
625
- function describeTransportError(error, url) {
626
- if (!(error instanceof Error))
627
- return String(error);
628
- const cause = error.cause;
629
- if (!(cause instanceof Error))
630
- return `${error.message} (${url})`;
631
- const code = cause.code;
632
- // The code and the message are each sometimes empty — an aggregate DNS failure carries a code and
633
- // no text, a TLS failure the reverse. Joined only when both are there, so the line never trails
634
- // off into a dangling separator.
635
- const parts = ['string' === typeof code && code.length > 0 ? code : '', cause.message].filter((part) => part.length > 0);
636
- const detail = parts.join(': ');
637
- return 0 === detail.length
638
- ? `${error.message} (${url})`
639
- : `${error.message} — ${detail} (${url})`;
640
- }
641
- /** One line a human can read, for the CLI and the daemon log. */
642
- export function describeSync(report) {
643
- if (report.error !== undefined)
644
- return `sync failed — ${report.error}`;
645
- const sent = [];
646
- if (report.runsSent > 0)
647
- sent.push(`${String(report.runsSent)} run(s)`);
648
- if (report.flowsSent > 0)
649
- sent.push(`${String(report.flowsSent)} flow(s)`);
650
- if (report.capsulesSent > 0)
651
- sent.push(`${String(report.capsulesSent)} capsule(s)`);
652
- if ((report.screenshotsSent ?? 0) > 0)
653
- sent.push(`${String(report.screenshotsSent)} screenshot(s)`);
654
- if (report.derivedSent.length > 0)
655
- sent.push(report.derivedSent.join(', '));
656
- /*
657
- * "Nothing to send" is a statement about the QUEUE, and it is false the moment the queue was full
658
- * and the server threw it away. Built from what was accepted, the sentence used to read "nothing
659
- * to send, 3 rejected" — something was very much sent, and the reader is told both that it was
660
- * not and nothing about why.
661
- */
662
- const rejectedCount = report.runsRejected.length + report.refused.length;
663
- const push = sent.length > 0
664
- ? `sent ${sent.join(' + ')}`
665
- : rejectedCount > 0
666
- ? 'nothing accepted'
667
- : true === report.localIsEmpty
668
- ? // Not the same statement as "nothing to send", which describes a repo that is simply up
669
- // to date. This one has never recorded anything, which for a LINKED repo usually means
670
- // the app announces no projectId and its runs are landing under a different root.
671
- 'nothing recorded here yet — if this app has been driven, it is reporting somewhere else'
672
- : 'nothing to send';
673
- const pull = 0 === report.pulled
674
- ? ''
675
- : `, pulled ${String(report.pulled)} decision(s)${report.morePending ? ' (more waiting)' : ''}`;
676
- return `${push}${pull}${problemsOf(report)
677
- .map((problem) => `, ${problem}`)
678
- .join('')}`;
679
- }
680
- /**
681
- * What went wrong, one phrase per kind of problem.
682
- *
683
- * One reason, not a count. A rejection count tells somebody they have a problem and nothing about
684
- * which problem — and these arrive from a BACKGROUND daemon, so the summary line is often the only
685
- * place anybody ever sees it. Rejections in one cycle almost always share a cause (a version skew
686
- * refuses every payload the same way), so the first reason plus the count is the whole story
687
- * without printing a line per item; `reticle sync` still lists them all.
688
- */
689
- function problemsOf(report) {
690
- const problems = [];
691
- const count = report.runsRejected.length + report.refused.length;
692
- if (count > 0) {
693
- const first = report.runsRejected[0]?.reason ?? report.refused[0]?.reason ?? 'no reason given';
694
- problems.push(`${String(count)} rejected — ${first}`);
695
- }
696
- const firstRefusal = report.notRetried[0];
697
- if (firstRefusal !== undefined)
698
- problems.push(`refused, not retried: ${String(report.notRetried.length)} run(s) (${firstRefusal.reason})`);
699
- for (const set of report.setsNotRetried)
700
- problems.push(`refused, not retried: ${String(set.count)} ${set.part}(s) (${set.reason})`);
701
- if (report.held.length > 0)
702
- problems.push(`not sent: ${report.held.join('; ')}`);
703
- return problems;
704
- }
@@ -1,5 +1,7 @@
1
1
  import { type SyncReport } from './sync-cycle.js';
2
2
  import { type ProjectCloud } from './cloud-config.js';
3
+ import { type FSWatcher } from 'node:fs';
4
+ import { BackgroundSyncState, type BackgroundSyncGate } from './background-sync.js';
3
5
  import { type UnsyncedRoot } from './unsynced-roots.js';
4
6
  import type { FileSystemPort } from '../project/fs/fs-port.js';
5
7
  interface SyncDaemonDeps {
@@ -20,12 +22,15 @@ interface SyncDaemonDeps {
20
22
  /** Resolve the link for a root that is not this daemon's own. Required to use `otherRoots`. */
21
23
  cloudFor?: (root: string) => Promise<ProjectCloud>;
22
24
  now?: () => number;
25
+ /** This CLI's version, stamped on each refusal so a new one asks the platform again. */
26
+ clientVersion?: string;
23
27
  intervalMs?: number;
24
28
  /**
25
- * Call `onChange` when this root's link file changes; returns how to stop. A re-link was read only
26
- * on the next idle tick, so a daemon kept syncing to the old link for up to a minute.
29
+ * Call `onChange` with the path (relative to the root) of anything that changes under it; returns
30
+ * how to stop, or undefined when the root cannot be watched YET (it does not exist), in which case
31
+ * the daemon tries again after each cycle. A re-link is one of these changes.
27
32
  */
28
- watchLink?: (onChange: () => void) => () => void;
33
+ watch?: (onChange: (path: string) => void) => (() => void) | undefined;
29
34
  /** Injected for the test; the real one is `fetch`. */
30
35
  request?: (url: string, init: {
31
36
  method: string;
@@ -34,7 +39,22 @@ interface SyncDaemonDeps {
34
39
  }) => Promise<{
35
40
  status: number;
36
41
  text: string;
42
+ etag?: string;
37
43
  }>;
44
+ /**
45
+ * May this root's work be sent now (opt-outs, link, sign-in)? Asked for every root on every tick.
46
+ * Absent: every resolved link may send, which is how `startSyncDaemon` behaved before.
47
+ */
48
+ gate?: (root: string, cloud: ProjectCloud) => BackgroundSyncGate;
49
+ }
50
+ /** Where the background sync of the daemon's own project stands, for `doctor` and the tests. */
51
+ export interface BackgroundSyncReport {
52
+ state: BackgroundSyncState;
53
+ why: string;
54
+ /** Network failures in a row; the backoff grows with it. */
55
+ failures: number;
56
+ lastError?: string;
57
+ lastPushAt?: number;
38
58
  }
39
59
  export interface SyncDaemon {
40
60
  /** Run one cycle now, whatever the timer is doing. Returns undefined when not linked. */
@@ -69,12 +89,22 @@ export interface SyncDaemon {
69
89
  * or not. An unlinked one is the case nothing will ever send, and it used to be skipped silently.
70
90
  */
71
91
  unsyncedRoots: () => readonly UnsyncedRoot[];
92
+ /** Where the background sync of this daemon's own root stands. */
93
+ background: () => BackgroundSyncReport;
72
94
  }
73
95
  /**
74
96
  * Start the loop. Safe to call for an UNLINKED project: it resolves the link on every tick and does
75
97
  * nothing until one appears, which is what lets `reticle link` take effect without a restart.
76
98
  */
77
99
  export declare function startSyncDaemon(deps: SyncDaemonDeps): SyncDaemon;
100
+ /**
101
+ * Flush, but return after `budgetMs` at the latest, and never throw.
102
+ *
103
+ * For a process on its way out. The cycle has per-call timeouts, but a stalled network still adds
104
+ * them up, and an exit must not wait on that. A cycle cut short loses nothing: the artifacts stay
105
+ * on disk, and the next daemon or `reticle sync` sends them.
106
+ */
107
+ export declare function flushWithin(daemon: Pick<SyncDaemon, 'flush'>, budgetMs: number): Promise<void>;
78
108
  /**
79
109
  * Start cloud sync for a daemon and hand back the two hooks the daemon wires to it.
80
110
  *
@@ -93,5 +123,22 @@ export declare function attachCloudSync(deps: {
93
123
  homeDir: string;
94
124
  env: NodeJS.ProcessEnv;
95
125
  otherRoots: () => readonly string[];
126
+ clientVersion: string;
127
+ /**
128
+ * The signed-in session's token for a platform address, or undefined. See `signedInTo`. Given by
129
+ * the background daemon only: `start()`'s one cycle on close also serves CI, keyed by the env.
130
+ */
131
+ signedIn?: (url: string) => string | undefined;
132
+ /** Injected for the tests. */
133
+ request?: SyncDaemonDeps['request'];
134
+ intervalMs?: number;
96
135
  }): SyncDaemon;
136
+ /**
137
+ * Watch a whole `.reticle` root, reporting each change by its path under the root.
138
+ *
139
+ * Returns undefined only when the root does not exist yet, so the caller tries again later. Where a
140
+ * recursive watch is unsupported (an old Node on Linux, a network filesystem) or fails later (too
141
+ * many files), it says so ONCE and returns a no-op: the backstop timer is the whole sync from then on.
142
+ */
143
+ export declare function watchReticleRoot(reticleRoot: string, onChange: (path: string) => void, watchFn?: (root: string, listener: (event: string, name: string | null) => void) => FSWatcher): (() => void) | undefined;
97
144
  export {};