@relayflows/sdk 2.0.42 → 2.0.43

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 (370) hide show
  1. package/dist/adapters/base.d.ts +12 -1
  2. package/dist/adapters/base.d.ts.map +1 -1
  3. package/dist/adapters/base.js +11 -1
  4. package/dist/adapters/base.js.map +1 -1
  5. package/dist/adapters/claude.d.ts.map +1 -1
  6. package/dist/adapters/claude.js +8 -4
  7. package/dist/adapters/claude.js.map +1 -1
  8. package/dist/adapters/codex.d.ts.map +1 -1
  9. package/dist/adapters/codex.js +15 -6
  10. package/dist/adapters/codex.js.map +1 -1
  11. package/dist/agent-resume.d.ts +8 -0
  12. package/dist/agent-resume.d.ts.map +1 -0
  13. package/dist/agent-resume.js +37 -0
  14. package/dist/agent-resume.js.map +1 -0
  15. package/dist/agent-transcript.d.ts.map +1 -1
  16. package/dist/agent-transcript.js +5 -3
  17. package/dist/agent-transcript.js.map +1 -1
  18. package/dist/authored-flow-error.d.ts +10 -2
  19. package/dist/authored-flow-error.d.ts.map +1 -1
  20. package/dist/authored-flow-error.js.map +1 -1
  21. package/dist/authored-flow-executor.d.ts.map +1 -1
  22. package/dist/authored-flow-executor.js +16 -2
  23. package/dist/authored-flow-executor.js.map +1 -1
  24. package/dist/authored-flow-loader.d.ts +6 -0
  25. package/dist/authored-flow-loader.d.ts.map +1 -1
  26. package/dist/authored-flow-loader.js +18 -6
  27. package/dist/authored-flow-loader.js.map +1 -1
  28. package/dist/authored-helper-effect.js +3 -2
  29. package/dist/authored-helper-effect.js.map +1 -1
  30. package/dist/authored-human.d.ts +2 -0
  31. package/dist/authored-human.d.ts.map +1 -1
  32. package/dist/authored-human.js +4 -0
  33. package/dist/authored-human.js.map +1 -1
  34. package/dist/authored-memory.d.ts +47 -2
  35. package/dist/authored-memory.d.ts.map +1 -1
  36. package/dist/authored-memory.js +394 -15
  37. package/dist/authored-memory.js.map +1 -1
  38. package/dist/authored-node-entry.js +6 -1
  39. package/dist/authored-node-entry.js.map +1 -1
  40. package/dist/authored-node-runner.d.ts +1 -1
  41. package/dist/authored-node-runner.d.ts.map +1 -1
  42. package/dist/authored-node-runner.js +6 -3
  43. package/dist/authored-node-runner.js.map +1 -1
  44. package/dist/authored-root-readback.d.ts +21 -0
  45. package/dist/authored-root-readback.d.ts.map +1 -0
  46. package/dist/authored-root-readback.js +90 -0
  47. package/dist/authored-root-readback.js.map +1 -0
  48. package/dist/authored-root.d.ts.map +1 -1
  49. package/dist/authored-root.js +53 -74
  50. package/dist/authored-root.js.map +1 -1
  51. package/dist/authored-step-output.d.ts +2 -0
  52. package/dist/authored-step-output.d.ts.map +1 -1
  53. package/dist/authored-step-output.js +1 -0
  54. package/dist/authored-step-output.js.map +1 -1
  55. package/dist/authored-worker-step.d.ts.map +1 -1
  56. package/dist/authored-worker-step.js +26 -4
  57. package/dist/authored-worker-step.js.map +1 -1
  58. package/dist/cli/check-helper-body.d.ts +2 -2
  59. package/dist/cli/check-helper-body.d.ts.map +1 -1
  60. package/dist/cli/check-helper-body.js +7 -2
  61. package/dist/cli/check-helper-body.js.map +1 -1
  62. package/dist/cli/check-helper-surface.d.ts +8 -0
  63. package/dist/cli/check-helper-surface.d.ts.map +1 -0
  64. package/dist/cli/check-helper-surface.js +37 -0
  65. package/dist/cli/check-helper-surface.js.map +1 -0
  66. package/dist/cli/check-triggers.d.ts +4 -3
  67. package/dist/cli/check-triggers.d.ts.map +1 -1
  68. package/dist/cli/check-triggers.js +7 -3
  69. package/dist/cli/check-triggers.js.map +1 -1
  70. package/dist/cli/check.d.ts +7 -1
  71. package/dist/cli/check.d.ts.map +1 -1
  72. package/dist/cli/check.js +35 -8
  73. package/dist/cli/check.js.map +1 -1
  74. package/dist/cli/cloud-answer.d.ts +9 -0
  75. package/dist/cli/cloud-answer.d.ts.map +1 -0
  76. package/dist/cli/cloud-answer.js +39 -0
  77. package/dist/cli/cloud-answer.js.map +1 -0
  78. package/dist/cli/cloud-connect-cli.d.ts +2 -1
  79. package/dist/cli/cloud-connect-cli.d.ts.map +1 -1
  80. package/dist/cli/cloud-connect-cli.js +10 -2
  81. package/dist/cli/cloud-connect-cli.js.map +1 -1
  82. package/dist/cli/cloud-human.d.ts +7 -0
  83. package/dist/cli/cloud-human.d.ts.map +1 -0
  84. package/dist/cli/cloud-human.js +22 -0
  85. package/dist/cli/cloud-human.js.map +1 -0
  86. package/dist/cli/cloud-live.d.ts +2 -2
  87. package/dist/cli/cloud-live.d.ts.map +1 -1
  88. package/dist/cli/cloud-live.js +9 -2
  89. package/dist/cli/cloud-live.js.map +1 -1
  90. package/dist/cli/cloud-mirror-session.d.ts.map +1 -1
  91. package/dist/cli/cloud-mirror-session.js +3 -2
  92. package/dist/cli/cloud-mirror-session.js.map +1 -1
  93. package/dist/cli/cloud-run.d.ts +1 -1
  94. package/dist/cli/cloud-run.d.ts.map +1 -1
  95. package/dist/cli/cloud-run.js +12 -0
  96. package/dist/cli/cloud-run.js.map +1 -1
  97. package/dist/cli/detached-record.d.ts +22 -0
  98. package/dist/cli/detached-record.d.ts.map +1 -0
  99. package/dist/cli/detached-record.js +57 -0
  100. package/dist/cli/detached-record.js.map +1 -0
  101. package/dist/cli/detached-run.d.ts +28 -0
  102. package/dist/cli/detached-run.d.ts.map +1 -0
  103. package/dist/cli/detached-run.js +122 -0
  104. package/dist/cli/detached-run.js.map +1 -0
  105. package/dist/cli/diagnostic-fold.d.ts +19 -0
  106. package/dist/cli/diagnostic-fold.d.ts.map +1 -0
  107. package/dist/cli/diagnostic-fold.js +19 -0
  108. package/dist/cli/diagnostic-fold.js.map +1 -0
  109. package/dist/cli/direct-run.d.ts.map +1 -1
  110. package/dist/cli/direct-run.js +16 -2
  111. package/dist/cli/direct-run.js.map +1 -1
  112. package/dist/cli/eval.d.ts +22 -0
  113. package/dist/cli/eval.d.ts.map +1 -0
  114. package/dist/cli/eval.js +131 -0
  115. package/dist/cli/eval.js.map +1 -0
  116. package/dist/cli/journal-timeout.d.ts +12 -0
  117. package/dist/cli/journal-timeout.d.ts.map +1 -0
  118. package/dist/cli/journal-timeout.js +42 -0
  119. package/dist/cli/journal-timeout.js.map +1 -0
  120. package/dist/cli/local-agent-remedy.d.ts.map +1 -1
  121. package/dist/cli/local-agent-remedy.js +5 -4
  122. package/dist/cli/local-agent-remedy.js.map +1 -1
  123. package/dist/cli/observer-session.d.ts +1 -0
  124. package/dist/cli/observer-session.d.ts.map +1 -1
  125. package/dist/cli/observer-session.js +8 -1
  126. package/dist/cli/observer-session.js.map +1 -1
  127. package/dist/cli/reuse.d.ts +8 -0
  128. package/dist/cli/reuse.d.ts.map +1 -1
  129. package/dist/cli/reuse.js +24 -0
  130. package/dist/cli/reuse.js.map +1 -1
  131. package/dist/cli/run.d.ts +8 -2
  132. package/dist/cli/run.d.ts.map +1 -1
  133. package/dist/cli/run.js +72 -56
  134. package/dist/cli/run.js.map +1 -1
  135. package/dist/cli/running-step.d.ts +9 -0
  136. package/dist/cli/running-step.d.ts.map +1 -0
  137. package/dist/cli/running-step.js +156 -0
  138. package/dist/cli/running-step.js.map +1 -0
  139. package/dist/cli-adapter.d.ts +3 -1
  140. package/dist/cli-adapter.d.ts.map +1 -1
  141. package/dist/cli-adapter.js +6 -2
  142. package/dist/cli-adapter.js.map +1 -1
  143. package/dist/cli-commands.d.ts +77 -11
  144. package/dist/cli-commands.d.ts.map +1 -1
  145. package/dist/cli-commands.js +41 -7
  146. package/dist/cli-commands.js.map +1 -1
  147. package/dist/cli-watch.d.ts +6 -2
  148. package/dist/cli-watch.d.ts.map +1 -1
  149. package/dist/cli-watch.js +7 -6
  150. package/dist/cli-watch.js.map +1 -1
  151. package/dist/cli.d.ts +22 -1
  152. package/dist/cli.d.ts.map +1 -1
  153. package/dist/cli.js +154 -50
  154. package/dist/cli.js.map +1 -1
  155. package/dist/cloud-answer.d.ts +35 -0
  156. package/dist/cloud-answer.d.ts.map +1 -0
  157. package/dist/cloud-answer.js +194 -0
  158. package/dist/cloud-answer.js.map +1 -0
  159. package/dist/cloud-auth-store.d.ts +46 -0
  160. package/dist/cloud-auth-store.d.ts.map +1 -0
  161. package/dist/cloud-auth-store.js +171 -0
  162. package/dist/cloud-auth-store.js.map +1 -0
  163. package/dist/cloud-http.d.ts +7 -16
  164. package/dist/cloud-http.d.ts.map +1 -1
  165. package/dist/cloud-http.js +59 -40
  166. package/dist/cloud-http.js.map +1 -1
  167. package/dist/cloud-human-wait.d.ts +10 -0
  168. package/dist/cloud-human-wait.d.ts.map +1 -0
  169. package/dist/cloud-human-wait.js +17 -0
  170. package/dist/cloud-human-wait.js.map +1 -0
  171. package/dist/cloud-mirror-transport.d.ts.map +1 -1
  172. package/dist/cloud-mirror-transport.js +1 -0
  173. package/dist/cloud-mirror-transport.js.map +1 -1
  174. package/dist/cloud-run-record.d.ts +7 -2
  175. package/dist/cloud-run-record.d.ts.map +1 -1
  176. package/dist/cloud-run-record.js +13 -5
  177. package/dist/cloud-run-record.js.map +1 -1
  178. package/dist/cloud-run.d.ts +9 -0
  179. package/dist/cloud-run.d.ts.map +1 -1
  180. package/dist/cloud-run.js +36 -2
  181. package/dist/cloud-run.js.map +1 -1
  182. package/dist/communication/local.d.ts.map +1 -1
  183. package/dist/communication/local.js +6 -1
  184. package/dist/communication/local.js.map +1 -1
  185. package/dist/communication/worker.d.ts.map +1 -1
  186. package/dist/communication/worker.js +12 -2
  187. package/dist/communication/worker.js.map +1 -1
  188. package/dist/compile.d.ts.map +1 -1
  189. package/dist/compile.js +5 -3
  190. package/dist/compile.js.map +1 -1
  191. package/dist/daemon-spec-validation.d.ts +31 -0
  192. package/dist/daemon-spec-validation.d.ts.map +1 -0
  193. package/dist/daemon-spec-validation.js +115 -0
  194. package/dist/daemon-spec-validation.js.map +1 -0
  195. package/dist/failure-kinds.d.ts +6 -4
  196. package/dist/failure-kinds.d.ts.map +1 -1
  197. package/dist/failure-kinds.js +17 -1
  198. package/dist/failure-kinds.js.map +1 -1
  199. package/dist/flow-eval-cloud.d.ts +25 -0
  200. package/dist/flow-eval-cloud.d.ts.map +1 -0
  201. package/dist/flow-eval-cloud.js +108 -0
  202. package/dist/flow-eval-cloud.js.map +1 -0
  203. package/dist/flow-eval-error.d.ts +7 -0
  204. package/dist/flow-eval-error.d.ts.map +1 -0
  205. package/dist/flow-eval-error.js +10 -0
  206. package/dist/flow-eval-error.js.map +1 -0
  207. package/dist/flow-eval-gate.d.ts +15 -0
  208. package/dist/flow-eval-gate.d.ts.map +1 -0
  209. package/dist/flow-eval-gate.js +102 -0
  210. package/dist/flow-eval-gate.js.map +1 -0
  211. package/dist/flow-eval-local.d.ts +54 -0
  212. package/dist/flow-eval-local.d.ts.map +1 -0
  213. package/dist/flow-eval-local.js +220 -0
  214. package/dist/flow-eval-local.js.map +1 -0
  215. package/dist/flow-eval-outcome.d.ts +22 -0
  216. package/dist/flow-eval-outcome.d.ts.map +1 -0
  217. package/dist/flow-eval-outcome.js +37 -0
  218. package/dist/flow-eval-outcome.js.map +1 -0
  219. package/dist/flow-eval-report.d.ts +106 -0
  220. package/dist/flow-eval-report.d.ts.map +1 -0
  221. package/dist/flow-eval-report.js +4 -0
  222. package/dist/flow-eval-report.js.map +1 -0
  223. package/dist/flow-eval-suite.d.ts +46 -0
  224. package/dist/flow-eval-suite.d.ts.map +1 -0
  225. package/dist/flow-eval-suite.js +144 -0
  226. package/dist/flow-eval-suite.js.map +1 -0
  227. package/dist/flow-eval-version.d.ts +59 -0
  228. package/dist/flow-eval-version.d.ts.map +1 -0
  229. package/dist/flow-eval-version.js +303 -0
  230. package/dist/flow-eval-version.js.map +1 -0
  231. package/dist/flow-eval.d.ts +41 -0
  232. package/dist/flow-eval.d.ts.map +1 -0
  233. package/dist/flow-eval.js +201 -0
  234. package/dist/flow-eval.js.map +1 -0
  235. package/dist/helper-operation-use.d.ts +18 -0
  236. package/dist/helper-operation-use.d.ts.map +1 -0
  237. package/dist/helper-operation-use.js +336 -0
  238. package/dist/helper-operation-use.js.map +1 -0
  239. package/dist/helper-preflight.d.ts.map +1 -1
  240. package/dist/helper-preflight.js +16 -2
  241. package/dist/helper-preflight.js.map +1 -1
  242. package/dist/helper-reference.d.ts +33 -0
  243. package/dist/helper-reference.d.ts.map +1 -1
  244. package/dist/helper-reference.js +5 -5
  245. package/dist/helper-reference.js.map +1 -1
  246. package/dist/index.d.ts +6 -1
  247. package/dist/index.d.ts.map +1 -1
  248. package/dist/index.js +5 -1
  249. package/dist/index.js.map +1 -1
  250. package/dist/journal-budgeted-reads.d.ts +41 -0
  251. package/dist/journal-budgeted-reads.d.ts.map +1 -0
  252. package/dist/journal-budgeted-reads.js +142 -0
  253. package/dist/journal-budgeted-reads.js.map +1 -0
  254. package/dist/journal-client.d.ts +34 -29
  255. package/dist/journal-client.d.ts.map +1 -1
  256. package/dist/journal-client.js +45 -151
  257. package/dist/journal-client.js.map +1 -1
  258. package/dist/journal-connection.d.ts +42 -0
  259. package/dist/journal-connection.d.ts.map +1 -0
  260. package/dist/journal-connection.js +209 -0
  261. package/dist/journal-connection.js.map +1 -0
  262. package/dist/journal-read-policy.d.ts +38 -0
  263. package/dist/journal-read-policy.d.ts.map +1 -0
  264. package/dist/journal-read-policy.js +126 -0
  265. package/dist/journal-read-policy.js.map +1 -0
  266. package/dist/named-gate-lowering.d.ts.map +1 -1
  267. package/dist/named-gate-lowering.js +2 -6
  268. package/dist/named-gate-lowering.js.map +1 -1
  269. package/dist/re2js-embedded.d.ts +3 -0
  270. package/dist/re2js-embedded.d.ts.map +1 -0
  271. package/dist/re2js-embedded.js +4 -0
  272. package/dist/re2js-embedded.js.map +1 -0
  273. package/dist/spec.d.ts +10 -0
  274. package/dist/spec.d.ts.map +1 -1
  275. package/dist/spec.js.map +1 -1
  276. package/dist/step-fields.d.ts +1 -1
  277. package/dist/step-fields.js +1 -1
  278. package/dist/step-fields.js.map +1 -1
  279. package/dist/validate.d.ts.map +1 -1
  280. package/dist/validate.js +15 -0
  281. package/dist/validate.js.map +1 -1
  282. package/dist/worker-cli.d.ts +3 -1
  283. package/dist/worker-cli.d.ts.map +1 -1
  284. package/dist/worker-cli.js +15 -5
  285. package/dist/worker-cli.js.map +1 -1
  286. package/dist/worker-lease.d.ts.map +1 -1
  287. package/dist/worker-lease.js +11 -3
  288. package/dist/worker-lease.js.map +1 -1
  289. package/dist/worker.js +1 -1
  290. package/dist/worker.js.map +1 -1
  291. package/package.json +5 -3
  292. package/src/adapters/base.ts +16 -1
  293. package/src/adapters/claude.ts +8 -4
  294. package/src/adapters/codex.ts +15 -6
  295. package/src/agent-resume.ts +36 -0
  296. package/src/agent-transcript.ts +5 -3
  297. package/src/authored-flow-error.ts +9 -0
  298. package/src/authored-flow-executor.ts +18 -1
  299. package/src/authored-flow-loader.ts +27 -10
  300. package/src/authored-helper-effect.ts +3 -2
  301. package/src/authored-human.ts +5 -0
  302. package/src/authored-memory.ts +445 -15
  303. package/src/authored-node-entry.ts +6 -1
  304. package/src/authored-node-runner.ts +6 -3
  305. package/src/authored-root-readback.ts +100 -0
  306. package/src/authored-root.ts +58 -85
  307. package/src/authored-step-output.ts +3 -0
  308. package/src/authored-worker-step.ts +29 -4
  309. package/src/cli/check-helper-body.ts +9 -3
  310. package/src/cli/check-helper-surface.ts +43 -0
  311. package/src/cli/check-triggers.ts +9 -4
  312. package/src/cli/check.ts +37 -7
  313. package/src/cli/cloud-answer.ts +40 -0
  314. package/src/cli/cloud-connect-cli.ts +9 -2
  315. package/src/cli/cloud-human.ts +24 -0
  316. package/src/cli/cloud-live.ts +10 -5
  317. package/src/cli/cloud-mirror-session.ts +3 -2
  318. package/src/cli/cloud-run.ts +10 -1
  319. package/src/cli/detached-record.ts +71 -0
  320. package/src/cli/detached-run.ts +131 -0
  321. package/src/cli/diagnostic-fold.ts +28 -0
  322. package/src/cli/direct-run.ts +14 -1
  323. package/src/cli/eval.ts +141 -0
  324. package/src/cli/journal-timeout.ts +49 -0
  325. package/src/cli/local-agent-remedy.ts +6 -4
  326. package/src/cli/observer-session.ts +7 -1
  327. package/src/cli/reuse.ts +25 -0
  328. package/src/cli/run.ts +75 -67
  329. package/src/cli/running-step.ts +140 -0
  330. package/src/cli-adapter.ts +7 -1
  331. package/src/cli-commands.ts +43 -7
  332. package/src/cli-watch.ts +8 -5
  333. package/src/cli.ts +158 -51
  334. package/src/cloud-answer.ts +202 -0
  335. package/src/cloud-auth-store.ts +185 -0
  336. package/src/cloud-http.ts +61 -50
  337. package/src/cloud-human-wait.ts +23 -0
  338. package/src/cloud-mirror-transport.ts +1 -0
  339. package/src/cloud-run-record.ts +14 -5
  340. package/src/cloud-run.ts +46 -2
  341. package/src/communication/local.ts +6 -1
  342. package/src/communication/worker.ts +11 -3
  343. package/src/compile.ts +5 -3
  344. package/src/daemon-spec-validation.ts +126 -0
  345. package/src/failure-kinds.ts +17 -1
  346. package/src/flow-eval-cloud.ts +114 -0
  347. package/src/flow-eval-error.ts +10 -0
  348. package/src/flow-eval-gate.ts +114 -0
  349. package/src/flow-eval-local.ts +236 -0
  350. package/src/flow-eval-outcome.ts +43 -0
  351. package/src/flow-eval-report.ts +102 -0
  352. package/src/flow-eval-suite.ts +169 -0
  353. package/src/flow-eval-version.ts +316 -0
  354. package/src/flow-eval.ts +234 -0
  355. package/src/helper-operation-use.ts +285 -0
  356. package/src/helper-preflight.ts +17 -2
  357. package/src/helper-reference.ts +6 -6
  358. package/src/index.ts +15 -1
  359. package/src/journal-budgeted-reads.ts +134 -0
  360. package/src/journal-client.ts +59 -166
  361. package/src/journal-connection.ts +222 -0
  362. package/src/journal-read-policy.ts +123 -0
  363. package/src/named-gate-lowering.ts +2 -6
  364. package/src/re2js-embedded.ts +3 -0
  365. package/src/spec.ts +10 -0
  366. package/src/step-fields.ts +1 -1
  367. package/src/validate.ts +14 -0
  368. package/src/worker-cli.ts +14 -3
  369. package/src/worker-lease.ts +11 -3
  370. package/src/worker.ts +1 -1
package/src/cli.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ import { unavailableValidation, validationAnnotation, type DaemonValidationMode } from './daemon-spec-validation.js';
3
+ import { foldUnprovableEffects, type FoldOptions } from './cli/diagnostic-fold.js';
4
+ import { startDetachedRun, emitDetachedHandle } from './cli/detached-run.js';
5
+ import { takeDetachedReceipt, type DetachedReceipt } from './cli/detached-record.js';
2
6
  import { withSubscriptionMetadata } from './cli/subscription-report.js';
3
7
  import { addPlugin } from './cli/add.js';
4
8
  import { parsePluginArgs, runPluginCommand, type PluginArgs } from './cli/plugin.js';
@@ -28,11 +32,13 @@ import {
28
32
  type RunProgress,
29
33
  type RunReport,
30
34
  } from './cli/run.js';
35
+ import { runCloudAnswerCli } from './cli/cloud-answer.js';
31
36
  import { answerFlow } from './cli/answer.js';
32
37
  import { checkAuthoredTriggers } from './cli/check-triggers.js';
33
38
  import { parseWebhookArgs, runServeWebhook } from './cli/serve-webhook.js';
34
39
  import { runDirectFlow } from './cli/direct-run.js';
35
40
  import { parseReplayArgs, replayJournal, type ReplayArgs } from './cli/replay.js';
41
+ import { parseEvalArgs, runEvalCli, type EvalArgs } from './cli/eval.js';
36
42
  import { parseStatusArgs, runStatus, type StatusArgs } from './cli/status.js';
37
43
  import {
38
44
  parseLogsArgs, parseRunsArgs, runCloudLogsCli, runCloudRunsCli, runCloudStatusCli,
@@ -53,7 +59,7 @@ import { parseBuildArgs, runBuild, type BuildArgs } from './cli/build.js';
53
59
  import { runHnMonitor } from './cli/hn-monitor.js';
54
60
  import { runTickRunner } from './cli/tick-runner.js';
55
61
  import { DEFAULT_DATA_DIR } from './daemon-connection.js';
56
- import { CLI_VERB_NAMES } from './cli-commands.js';
62
+ import { CLI_VERB_NAMES, isGlobalOption } from './cli-commands.js';
57
63
  import { isAgentCapacity } from './worker-slots.js';
58
64
  import {
59
65
  mintObserverUrl,
@@ -89,6 +95,7 @@ export type ParsedArgs =
89
95
  | { command: 'add'; value: string }
90
96
  | PluginArgs
91
97
  | ReplayArgs
98
+ | EvalArgs
92
99
  | StatusArgs
93
100
  | BuildArgs
94
101
  | DeployArgs
@@ -103,9 +110,10 @@ export type ParsedArgs =
103
110
  | CloudScheduleArgs
104
111
  | { command: 'schedules'; json: boolean }
105
112
  | { command: 'unschedule'; scheduleId: string; json: boolean }
106
- | { command: 'check'; json: boolean; watch: boolean; value: string }
107
- | { command: 'run'; bucket: string | undefined; reuseFromRunId: string | undefined; localAgent: boolean; agentCapacity: number | undefined; dataDir: string; input: string | undefined; json: boolean; spawn: boolean; noObserverLink: boolean; cloudMirror: boolean; allowHumanInfluenced: boolean; value: string }
108
- | { command: 'resume'; localAgent: boolean; agentCapacity: number | undefined; dataDir: string; json: boolean; spawn: boolean; noObserverLink: boolean; cloudMirror: boolean; allowHumanInfluenced: boolean; value: string }
113
+ | { command: 'check'; json: boolean; watch: boolean; explainWarnings: boolean; daemonValidation: DaemonValidationMode; value: string }
114
+ | { command: 'run'; detach?: boolean; bucket: string | undefined; reuseFromRunId: string | undefined; localAgent: boolean; agentCapacity: number | undefined; dataDir: string; input: string | undefined; json: boolean; spawn: boolean; noObserverLink: boolean; cloudMirror: boolean; allowHumanInfluenced: boolean; value: string }
115
+ | { command: 'resume'; detach?: boolean; localAgent: boolean; agentCapacity: number | undefined; dataDir: string; json: boolean; spawn: boolean; noObserverLink: boolean; cloudMirror: boolean; allowHumanInfluenced: boolean; value: string }
116
+ | { command: 'cloud-answer'; runId: string; answer: boolean; json: boolean; note?: string; source?: string }
109
117
  | { command: 'answer'; dataDir: string; json: boolean; spawn: boolean; note: string | undefined; by: string | undefined; runId: string; waitId: string; answer: boolean }
110
118
  | RunsArgs
111
119
  | LogsArgs
@@ -135,18 +143,20 @@ const USAGE = [
135
143
  'flows schedules [--json]',
136
144
  'flows unschedule [--json] <schedule-id>',
137
145
  'flows deploy <flow>@sha256:<digest> --to <file-bucket-uri>',
138
- 'flows run <flow>@sha256:<digest> [--bucket <file-bucket-uri>] [--data-dir <dir>] [--json]',
139
- 'flows check [--watch] [--json] <flow.ts|flow.yaml|spec.json>',
146
+ 'flows run <flow>@sha256:<digest> [--bucket <file-bucket-uri>] [--data-dir <dir>] [--json] [--detach]',
147
+ 'flows check [--against-daemon|--no-daemon-check] [--watch] [--json] [--explain-warnings] <flow.ts|flow.yaml|spec.json>',
140
148
  'flows serve-webhook --data-dir <dir> --port <p> [--allow <name>[,<name>]]',
141
- 'flows run [--json] [--no-spawn] [--no-observer-link] [--cloud-mirror] [--data-dir <dir>] [--local-agent [--agent-capacity <n>]] [--reuse-from <run-id>] <flow.yaml|spec.json>',
149
+ 'flows run [--json] [--no-spawn] [--no-observer-link] [--cloud-mirror] [--detach] [--data-dir <dir>] [--local-agent [--agent-capacity <n>]] [--reuse-from <run-id>] <flow.yaml|spec.json>',
142
150
  'flows run --cloud [--json] [--wait] [--sync-code] [--no-connect] <flow.yaml|spec.json>',
143
151
  'flows run --cloud [--json] [--wait] [--sync-code] [--no-connect] <flow.ts> --input <inline-json-or-file>',
144
152
  'flows sync [--json] [--dry-run] [--dir <path>] <run-id>',
145
- 'flows run [--json] [--no-spawn] [--no-observer-link] [--cloud-mirror] [--data-dir <dir>] [--local-agent [--agent-capacity <n>]] <flow.ts> --input <inline-json-or-file>',
153
+ 'flows run [--json] [--no-spawn] [--no-observer-link] [--cloud-mirror] [--detach] [--data-dir <dir>] [--local-agent [--agent-capacity <n>]] <flow.ts> --input <inline-json-or-file>',
146
154
  'flows tick start --schedule-id <id> --interval-ms <ms> [--epoch-ms <ms>] [--max-catch-up <n>] [--poll-interval-ms <ms>] [--data-dir <dir>] <spec.json>',
147
- 'flows resume [--allow-human-influenced] [--json] [--no-spawn] [--no-observer-link] [--cloud-mirror] [--data-dir <dir>] [--local-agent [--agent-capacity <n>]] <run-id>',
155
+ 'flows resume [--allow-human-influenced] [--json] [--no-spawn] [--no-observer-link] [--cloud-mirror] [--detach] [--data-dir <dir>] [--local-agent [--agent-capacity <n>]] <run-id>',
156
+ 'flows answer --cloud [--json] [--note <text>] [--source <path>] <run-id> <yes|no>',
148
157
  'flows answer [--json] [--no-spawn] [--data-dir <dir>] [--note <text>] [--by <identity>] <run-id> <wait-id> <yes|no>',
149
158
  'flows replay [--allow-human-influenced] [--json] [--data-dir <dir>] <run-id> [--at <step-id>]',
159
+ 'flows eval [--json] [--cloud] [--data-dir <dir>] [--local-agent] [--concurrency <n>] [--report <file>] [--baseline <report.json>] [--expect-version <sha256:hex>] <flow> --cases <suite.json>',
150
160
  'flows status [--json] [--data-dir <dir>] [--tail <n>] [<run-id>]',
151
161
  'flows status --cloud [--json] [--watch] <run-id>',
152
162
  'flows runs [--limit <n>] [--json]',
@@ -173,6 +183,13 @@ const PROCESS_IO: CliIo = {
173
183
 
174
184
  /** Optional knobs for an embedded caller. `bin/flows.js` passes none. */
175
185
  export interface RunCliOptions {
186
+ /** Internal standalone child receipt; absent in ordinary embedded invocations. */
187
+ detachedReceipt?: DetachedReceipt;
188
+ /**
189
+ * Argv that re-executes this CLI for `--detach`. A compiled executable
190
+ * passes `[process.execPath]`; the default re-runs Node with this entry.
191
+ */
192
+ selfCommand?: readonly string[];
176
193
  /**
177
194
  * Version to print for a self-contained executable that cannot read the
178
195
  * installed package manifest. Normal package entrypoints leave this unset.
@@ -220,11 +237,11 @@ export async function runCli(
220
237
  io: CliIo = PROCESS_IO,
221
238
  options: RunCliOptions = {},
222
239
  ): Promise<CliExitCode> {
223
- if (args.length === 1 && (args[0] === '--version' || args[0] === '-V')) {
240
+ if (isGlobalOption(args, '--version')) {
224
241
  io.stdout(options.version ?? packageVersion());
225
242
  return 0;
226
243
  }
227
- if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
244
+ if (isGlobalOption(args, '--help')) {
228
245
  io.stdout(USAGE);
229
246
  return 0;
230
247
  }
@@ -236,6 +253,18 @@ export async function runCli(
236
253
  return 2;
237
254
  }
238
255
 
256
+ if ((parsed.command === 'run' || parsed.command === 'resume') && parsed.detach) {
257
+ const result = await startDetachedRun(args, {
258
+ ...parsed, ...(options.selfCommand === undefined ? {} : { selfCommand: options.selfCommand }),
259
+ });
260
+ if ('execution' in result) {
261
+ emitRunReport(result.execution, parsed.json, io);
262
+ return result.execution.exitCode;
263
+ }
264
+ emitDetachedHandle(result.handle, parsed.json, io);
265
+ return 0;
266
+ }
267
+
239
268
  if (parsed.command === 'add') return addPlugin(parsed.value, io);
240
269
  if (parsed.command === 'plugin') return runPluginCommand(parsed, io);
241
270
 
@@ -256,6 +285,10 @@ export async function runCli(
256
285
  if (parsed.command === 'schedules') return runCloudSchedulesCli(parsed, io);
257
286
  if (parsed.command === 'unschedule') return runCloudUnscheduleCli(parsed, io);
258
287
  if (parsed.command === 'replay') return replayJournal(parsed, io);
288
+ if (parsed.command === 'eval') {
289
+ const evaluation = parsed;
290
+ return withInterrupt(options.signal, (signal) => runEvalCli(evaluation, io, signal));
291
+ }
259
292
  // Daemon-free like `check`: reads one journal file and nothing else, so it
260
293
  // works inside a step of a run whose daemon is gone (kernel/DAEMON-LIFECYCLE.md §4).
261
294
  if (parsed.command === 'status') {
@@ -275,6 +308,9 @@ export async function runCli(
275
308
  ? withInterrupt(options.signal, (signal) => runCloudLogsFollow(logs, io, { signal }))
276
309
  : runCloudLogsCli(logs, io);
277
310
  }
311
+ if (parsed.command === 'cloud-answer') {
312
+ return withInterrupt(options.signal, signal => runCloudAnswerCli(parsed, io, signal));
313
+ }
278
314
  if (parsed.command === 'answer') {
279
315
  const execution = await answerFlow(parsed.runId, parsed.waitId, parsed.answer, parsed.dataDir, {
280
316
  ...(parsed.note === undefined ? {} : { note: parsed.note }),
@@ -289,21 +325,21 @@ export async function runCli(
289
325
 
290
326
  if (parsed.command === 'check') {
291
327
  if (parsed.watch) {
292
- return withInterrupt(options.signal, (signal) => watchCheck(parsed.value, parsed.json, io, signal));
328
+ return withInterrupt(options.signal, (signal) => watchCheck(parsed.value, { json: parsed.json, explainWarnings: parsed.explainWarnings }, io, signal, parsed.daemonValidation));
293
329
  }
294
- // Deliberately daemon-free (kernel/DAEMON-LIFECYCLE.md §4). `checkFlow` is
295
- // a compile-and-preflight that opens no daemon socket, and the parser
296
- // refuses `--data-dir` on `check`, so there is no data dir to attach to.
297
- // `flows check` keeps working with no daemon, no relayflowd binary and no
298
- // data directory at all -- a property worth keeping, not an omission.
299
- // Only this invocation opts into `agent_worker_unresolved`: `flows check`
300
- // attaches no worker and, being daemon-free, cannot see one attached
301
- // elsewhere. The authored `.flow.ts` path checks header declarations
302
- // without compiling steps, so it has no agent steps to count.
330
+ // Stateless binary validation opens no socket or data dir (DAEMON-LIFECYCLE §4).
331
+ // Authored bodies are only lowered at run time, so their acceptance is unproven.
303
332
  const checked = /\.(?:[cm]?[jt]s)$/.test(parsed.value)
304
333
  ? await checkAuthoredFlowComposed(parsed.value)
305
- : checkFlow(parsed.value, { warnUnresolvedAgentWorker: true });
306
- emitCheckReport(checked.report, parsed.json, io);
334
+ : checkFlow(parsed.value, { warnUnresolvedAgentWorker: true, warnUnresolvedHelperCredential: true, daemonValidation: parsed.daemonValidation });
335
+ if (checked.report.validation === undefined) {
336
+ const unavailable = unavailableValidation(parsed.daemonValidation,
337
+ checked.report.ok ? (parsed.daemonValidation === 'off' ? 'disabled' : 'authored_body') : 'not_reached');
338
+ checked.report.validation = unavailable.validation;
339
+ if (checked.report.ok) checked.report.diagnostics.push(...unavailable.diagnostics);
340
+ checked.report.ok = checked.report.ok && !checked.report.diagnostics.some(d => d.severity === 'refusal');
341
+ }
342
+ emitCheckReport(checked.report, parsed.json, io, parsed.explainWarnings);
307
343
  return checked.report.ok ? 0 : 2;
308
344
  }
309
345
 
@@ -338,7 +374,9 @@ export async function runCli(
338
374
  // not here. Hoisting it above the dispatch would start a daemon as a side
339
375
  // effect of an invocation that is about to be refused for bad input.
340
376
  const startedSteps = new Map<string, number>();
341
- const observer = parsed.noObserverLink ? undefined : createObserverSession(parsed.command, io);
377
+ const observer = parsed.noObserverLink ? undefined : createObserverSession(parsed.command, io, process.env, {
378
+ onObserverUrl: url => options.detachedReceipt?.observerUrl(url),
379
+ });
342
380
  const runnerLog: string[] = [];
343
381
  // Opt-in, unlike the observer link beside it. The observer is the default
344
382
  // way to watch a local run: it is free, it needs only a workspace key, and
@@ -380,17 +418,25 @@ export async function runCli(
380
418
  localAgent: parsed.localAgent,
381
419
  ...(parsed.agentCapacity === undefined ? {} : { agentCapacity: parsed.agentCapacity }),
382
420
  onProgress: showProgress,
383
- ...(observer === undefined && mirror === undefined ? {} : {
421
+ ...(observer === undefined && mirror === undefined && options.detachedReceipt === undefined ? {} : {
384
422
  onJournalEntry: (entry: JournalEvent) => {
423
+ // Resume's watch replays history before admission; it is not a receipt.
424
+ if (parsed.command === 'run' && entry.entry_type === 'run.spawned') {
425
+ options.detachedReceipt?.started({ runId: entry.run_id });
426
+ }
385
427
  observer?.onJournalEntry(entry);
386
428
  mirror?.onJournalEntry(entry);
387
429
  },
388
430
  onRunStarted: (run: { runId: string; flow: string; resumed?: boolean }) => {
431
+ options.detachedReceipt?.started(run);
389
432
  observer?.onRunStarted(run);
390
433
  mirror?.onRunStarted(run);
391
434
  },
392
- ...(mirror === undefined ? {} : {
393
- onRunReceipt: (run: { runId: string }) => { mirror.onRunStarted(run); },
435
+ ...(mirror === undefined && options.detachedReceipt === undefined ? {} : {
436
+ onRunReceipt: (run: { runId: string }) => {
437
+ options.detachedReceipt?.started(run);
438
+ mirror?.onRunStarted(run);
439
+ },
394
440
  }),
395
441
  }),
396
442
  onWait: (progress: RunProgress) => {
@@ -407,6 +453,7 @@ export async function runCli(
407
453
  ? await runDirectFlow(parsed.value, parsed.input, parsed.dataDir, lifecycle)
408
454
  : await runFlow(parsed.value, parsed.dataDir, lifecycle)
409
455
  : await resumeFlow(parsed.value, parsed.dataDir, lifecycle);
456
+ options.detachedReceipt?.finished(execution);
410
457
  // The link is scoped to the run's channel, so it exists only once the run
411
458
  // does. `finish` drains the projection and settles the mint, both bounded;
412
459
  // it never rejects (see `createObserverSession`).
@@ -445,18 +492,32 @@ export async function runCli(
445
492
  * aspects' coverage.
446
493
  */
447
494
  async function checkAuthoredFlowComposed(path: string): Promise<{ report: CheckReport }> {
448
- const helper = await checkHelperBody(path);
495
+ const invocation = { warnUnresolvedHelperCredential: true };
496
+ const helper = await checkHelperBody(path, invocation);
449
497
  if (!helper.report.ok) return helper;
450
498
  // The activity checker loads the TypeScript compiler. YAML checks and
451
499
  // unrelated CLI commands should not pay that startup cost on every run.
452
500
  const { checkAuthoredActivities } = await import('./cli/check-activities.js');
453
501
  const activities = await checkAuthoredActivities(path);
454
- if (!activities.report.ok) return activities;
502
+ if (!activities.report.ok) {
503
+ // Keep what is already provable about the flow: its helper warnings and
504
+ // integration requirements, so one check reports both problems.
505
+ const requirements = isAuthoredFlowPath(path)
506
+ ? (await checkAuthoredTriggers(path, invocation)).report.requirements : undefined;
507
+ return { report: { ...activities.report,
508
+ diagnostics: [...helper.report.diagnostics, ...activities.report.diagnostics],
509
+ ...(requirements === undefined ? {} : { requirements }) } };
510
+ }
455
511
  const mcp = await checkTypeScriptFlow(path);
456
512
  const triggers = isAuthoredFlowPath(path)
457
- ? await checkAuthoredTriggers(path)
513
+ ? await checkAuthoredTriggers(path, invocation)
458
514
  : undefined;
459
515
  const triggerDiagnostics = triggers?.report.diagnostics ?? [];
516
+ // The trigger leg normally repeats the helper diagnostics; keep any it did not
517
+ // (an early trigger or config failure reports none of them).
518
+ const repeated = new Set(triggerDiagnostics.map(diagnostic => `${diagnostic.kind}\u0000${diagnostic.message}`));
519
+ const helperDiagnostics = helper.report.diagnostics
520
+ .filter(diagnostic => !repeated.has(`${diagnostic.kind}\u0000${diagnostic.message}`));
460
521
  const triggerOk = triggers?.report.ok ?? true;
461
522
  return {
462
523
  report: {
@@ -467,7 +528,7 @@ async function checkAuthoredFlowComposed(path: string): Promise<{ report: CheckR
467
528
  // The authored definition sees helper flags, body use and `cli:`
468
529
  // declarations; the compiled view underneath knows only its steps.
469
530
  ...(triggers?.report.requirements === undefined ? {} : { requirements: triggers.report.requirements }),
470
- diagnostics: [...helper.report.diagnostics, ...activities.report.diagnostics, ...mcp.report.diagnostics, ...triggerDiagnostics],
531
+ diagnostics: [...helperDiagnostics, ...activities.report.diagnostics, ...mcp.report.diagnostics, ...triggerDiagnostics],
471
532
  ok: helper.report.ok && activities.report.ok && mcp.report.ok && triggerOk,
472
533
  },
473
534
  };
@@ -611,6 +672,7 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
611
672
  if (command === 'add') return args.length === 2 ? { command: 'add', value: args[1]! } : undefined;
612
673
  if (command === 'plugin') return parsePluginArgs(args.slice(1));
613
674
  if (command === 'replay') return parseReplayArgs(args.slice(1));
675
+ if (command === 'eval') return parseEvalArgs(args.slice(1));
614
676
  if (command === 'status') return parseStatusArgs(args.slice(1));
615
677
  if (command === 'runs') return parseRunsArgs(args.slice(1));
616
678
  if (command === 'logs') return parseLogsArgs(args.slice(1));
@@ -661,11 +723,14 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
661
723
 
662
724
  let json = false;
663
725
  let watch = false;
726
+ let daemonValidation: DaemonValidationMode | undefined;
727
+ let explainWarnings = false;
664
728
  let cloud = false;
665
729
  let wait = false;
666
730
  let syncCode = false;
667
731
  let noConnect = false;
668
732
  let localAgent = false;
733
+ let detach = false;
669
734
  let agentCapacity: number | undefined;
670
735
  let allowHumanInfluenced = false;
671
736
  let dataDir = DEFAULT_DATA_DIR;
@@ -689,6 +754,11 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
689
754
  else noConnect = true;
690
755
  continue;
691
756
  }
757
+ if (argument === '--detach') {
758
+ if (command === 'check' || detach) return undefined;
759
+ detach = true;
760
+ continue;
761
+ }
692
762
  if (argument === '--allow-human-influenced') {
693
763
  if (command === 'check' || allowHumanInfluenced) return undefined;
694
764
  allowHumanInfluenced = true;
@@ -707,6 +777,16 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
707
777
  if (!isAgentCapacity(agentCapacity)) return undefined;
708
778
  continue;
709
779
  }
780
+ if (argument === '--against-daemon' || argument === '--no-daemon-check') {
781
+ if (command !== 'check' || daemonValidation !== undefined) return undefined;
782
+ daemonValidation = argument === '--against-daemon' ? 'required' : 'off';
783
+ continue;
784
+ }
785
+ if (argument === '--explain-warnings') {
786
+ if (command !== 'check' || explainWarnings) return undefined;
787
+ explainWarnings = true;
788
+ continue;
789
+ }
710
790
  if (argument === '--watch') {
711
791
  if (command !== 'check' || watch) return undefined;
712
792
  watch = true;
@@ -782,7 +862,7 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
782
862
  // observer-link opt-out -- describes nothing there and is refused rather
783
863
  // than ignored. `--input` is the authored body's argument and travels with
784
864
  // the source, so it is accepted exactly where a local run accepts it.
785
- if (allowHumanInfluenced || sawDataDir || !spawn || localAgent || noObserverLink || cloudMirror
865
+ if (detach || allowHumanInfluenced || sawDataDir || !spawn || localAgent || noObserverLink || cloudMirror
786
866
  || reuseFromRunId !== undefined) return undefined;
787
867
  if (sawInput && !isAuthoredFlowPath(positionals[0]!)) return undefined;
788
868
  return { command: 'cloud-run', value: positionals[0]!, json, wait, input, syncCode, noConnect };
@@ -792,10 +872,12 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
792
872
 
793
873
  if (command === 'run' && input !== undefined && !isAuthoredFlowPath(positionals[0]!)) return undefined;
794
874
  return command === 'check'
795
- ? { command, json, watch, value: positionals[0]! }
875
+ ? { command, json, watch, explainWarnings, value: positionals[0]!,
876
+ daemonValidation: daemonValidation ?? (process.env['FLOWS_NO_DAEMON_CHECK'] === '1' ? 'off'
877
+ : process.env['FLOWS_CHECK_AGAINST_DAEMON'] === '1' ? 'required' : 'auto') }
796
878
  : command === 'run'
797
- ? { command, bucket, reuseFromRunId, localAgent, agentCapacity, dataDir, input, json, spawn, noObserverLink, cloudMirror, allowHumanInfluenced, value: positionals[0]! }
798
- : { command, localAgent, agentCapacity, dataDir, json, spawn, noObserverLink, cloudMirror, allowHumanInfluenced, value: positionals[0]! };
879
+ ? { command, ...(detach ? { detach } : {}), bucket, reuseFromRunId, localAgent, agentCapacity, dataDir, input, json, spawn, noObserverLink, cloudMirror, allowHumanInfluenced, value: positionals[0]! }
880
+ : { command, ...(detach ? { detach } : {}), localAgent, agentCapacity, dataDir, json, spawn, noObserverLink, cloudMirror, allowHumanInfluenced, value: positionals[0]! };
799
881
  }
800
882
 
801
883
  /**
@@ -808,6 +890,8 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
808
890
  */
809
891
  function parseAnswerArgs(rest: readonly string[]): ParsedArgs | undefined {
810
892
  let json = false;
893
+ let cloud = false;
894
+ let source: string | undefined;
811
895
  let spawn = true;
812
896
  let dataDir = DEFAULT_DATA_DIR;
813
897
  let sawDataDir = false;
@@ -816,6 +900,18 @@ function parseAnswerArgs(rest: readonly string[]): ParsedArgs | undefined {
816
900
  const positionals: string[] = [];
817
901
  for (let index = 0; index < rest.length; index += 1) {
818
902
  const argument = rest[index]!;
903
+ if (argument === '--cloud') {
904
+ if (cloud) return undefined;
905
+ cloud = true;
906
+ continue;
907
+ }
908
+ if (argument === '--source') {
909
+ const value = rest[index + 1];
910
+ if (source !== undefined || value === undefined || value.startsWith('-')) return undefined;
911
+ source = value;
912
+ index += 1;
913
+ continue;
914
+ }
819
915
  if (argument === '--json') {
820
916
  if (json) return undefined;
821
917
  json = true;
@@ -851,6 +947,14 @@ function parseAnswerArgs(rest: readonly string[]): ParsedArgs | undefined {
851
947
  if (argument.startsWith('-')) return undefined;
852
948
  positionals.push(argument);
853
949
  }
950
+ if (cloud) {
951
+ if (sawDataDir || !spawn || by !== undefined || positionals.length !== 2) return undefined;
952
+ const [runId, word] = positionals as [string, string];
953
+ const answer = word === 'yes' || word === 'true' ? true : word === 'no' || word === 'false' ? false : undefined;
954
+ if (answer === undefined) return undefined;
955
+ return { command: 'cloud-answer', runId, answer, json, ...(note === undefined ? {} : { note }), ...(source === undefined ? {} : { source }) };
956
+ }
957
+ if (source !== undefined) return undefined;
854
958
  if (positionals.length !== 3) return undefined;
855
959
  const [runId, waitId, word] = positionals as [string, string, string];
856
960
  const answer = word === 'yes' || word === 'true' ? true : word === 'no' || word === 'false' ? false : undefined;
@@ -1034,15 +1138,12 @@ function parseTickArgs(rest: readonly string[]): ParsedArgs | undefined {
1034
1138
  }
1035
1139
 
1036
1140
  /**
1037
- * `agent_worker_unresolved` reads as a footnote to `REQUIRES codex (step
1038
- * "implement"), …`: that line already names the steps that need an agent
1039
- * worker, and this says what has to be true for one to be attached. So the
1040
- * plain-text pass holds it back and emits it in that position, exactly once —
1041
- * the leading batch below skips it rather than printing it twice. JSON mode
1042
- * returns before any of this and keeps the single ordered diagnostics array.
1141
+ * Worker and helper warnings annotate REQUIRES. Hold them back until that
1142
+ * line, then emit each once. JSON keeps the single diagnostics array.
1043
1143
  */
1044
- function isWorkerSurfaceWarning(diagnostic: CheckReport['diagnostics'][number]): boolean {
1045
- return diagnostic.kind === 'agent_worker_unresolved';
1144
+ function isRequiresFootnote(diagnostic: CheckReport['diagnostics'][number]): boolean {
1145
+ return diagnostic.kind === 'agent_worker_unresolved'
1146
+ || diagnostic.kind === 'helper_credential_unresolved';
1046
1147
  }
1047
1148
 
1048
1149
  const MODEL_PROVENANCE: Readonly<Record<CliModelSource, string>> = {
@@ -1056,14 +1157,18 @@ function modelProvenance(source: CliModelSource | undefined): string {
1056
1157
  return source === undefined ? '' : ` from ${MODEL_PROVENANCE[source]}`;
1057
1158
  }
1058
1159
 
1059
- function emitCheckReport(report: CheckReport, json: boolean, io: CliIo): void {
1160
+ function emitCheckReport(report: CheckReport, json: boolean, io: CliIo, explainWarnings = false): void {
1161
+ const foldOptions: FoldOptions = {
1162
+ fold: true, explain: explainWarnings,
1163
+ ...(report.gates.length > 0 ? { totalSteps: report.gates.length } : {}),
1164
+ };
1060
1165
  if (json) {
1061
- emitDiagnostics(report.diagnostics, io);
1166
+ emitDiagnostics(report.diagnostics, io, foldOptions);
1062
1167
  io.stdout(JSON.stringify(report));
1063
1168
  return;
1064
1169
  }
1065
- const deferred = report.diagnostics.filter(isWorkerSurfaceWarning);
1066
- emitDiagnostics(report.diagnostics.filter((diagnostic) => !isWorkerSurfaceWarning(diagnostic)), io);
1170
+ const deferred = report.diagnostics.filter(isRequiresFootnote);
1171
+ emitDiagnostics(report.diagnostics.filter((diagnostic) => !isRequiresFootnote(diagnostic)), io, foldOptions);
1067
1172
  for (const gate of report.gates) {
1068
1173
  // A gate that accepts every output is legal, but it must not read like a
1069
1174
  // gate that judges something.
@@ -1109,8 +1214,8 @@ function emitCheckReport(report: CheckReport, json: boolean, io: CliIo): void {
1109
1214
  // the hosted verbs check the same list against Cloud before submitting.
1110
1215
  const requires = report.requirements === undefined ? '' : describeFlowRequirements(report.requirements);
1111
1216
  if (requires) io.stdout(`REQUIRES ${requires}`);
1112
- emitDiagnostics(deferred, io);
1113
- if (report.ok) io.stdout(`CHECK PASSED ${report.path ?? ''}`.trimEnd());
1217
+ emitDiagnostics(deferred, io, foldOptions);
1218
+ if (report.ok) io.stdout(`CHECK PASSED ${report.path ?? ''}`.trimEnd() + validationAnnotation(report.validation));
1114
1219
  }
1115
1220
 
1116
1221
  function emitRunReport(
@@ -1173,8 +1278,9 @@ function emitRunReport(
1173
1278
  function emitDiagnostics(
1174
1279
  diagnostics: CheckReport['diagnostics'] | RunReport['diagnostics'],
1175
1280
  io: CliIo,
1281
+ options: FoldOptions = {},
1176
1282
  ): void {
1177
- for (const diagnostic of diagnostics) {
1283
+ for (const diagnostic of foldUnprovableEffects(diagnostics, options)) {
1178
1284
  io.stderr(`${diagnosticLabel(diagnostic.severity)} [${diagnostic.kind}] ${diagnostic.message}`);
1179
1285
  }
1180
1286
  }
@@ -1199,7 +1305,8 @@ function isDirectInvocation(entryPath: string | undefined): boolean {
1199
1305
  }
1200
1306
 
1201
1307
  if (isDirectInvocation(process.argv[1])) {
1202
- void runCli(process.argv.slice(2)).then((exitCode) => {
1308
+ const detachedReceipt = takeDetachedReceipt();
1309
+ void runCli(process.argv.slice(2), PROCESS_IO, { detachedReceipt }).then((exitCode) => {
1203
1310
  process.exitCode = exitCode;
1204
1311
  });
1205
1312
  }