@relayflows/sdk 2.0.9 → 2.0.11

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 (485) hide show
  1. package/dist/adapters/base.d.ts +46 -0
  2. package/dist/adapters/base.d.ts.map +1 -0
  3. package/dist/adapters/base.js +15 -0
  4. package/dist/adapters/base.js.map +1 -0
  5. package/dist/adapters/claude.d.ts +5 -0
  6. package/dist/adapters/claude.d.ts.map +1 -0
  7. package/dist/adapters/claude.js +44 -0
  8. package/dist/adapters/claude.js.map +1 -0
  9. package/dist/adapters/codex.d.ts +5 -0
  10. package/dist/adapters/codex.d.ts.map +1 -0
  11. package/dist/adapters/codex.js +45 -0
  12. package/dist/adapters/codex.js.map +1 -0
  13. package/dist/adapters/index.d.ts +14 -0
  14. package/dist/adapters/index.d.ts.map +1 -0
  15. package/dist/adapters/index.js +29 -0
  16. package/dist/adapters/index.js.map +1 -0
  17. package/dist/adapters/wrapper.d.ts +12 -0
  18. package/dist/adapters/wrapper.d.ts.map +1 -0
  19. package/dist/adapters/wrapper.js +31 -0
  20. package/dist/adapters/wrapper.js.map +1 -0
  21. package/dist/agent-relay-receipt.d.ts +24 -0
  22. package/dist/agent-relay-receipt.d.ts.map +1 -0
  23. package/dist/agent-relay-receipt.js +66 -0
  24. package/dist/agent-relay-receipt.js.map +1 -0
  25. package/dist/agent-relay-state.d.ts +18 -0
  26. package/dist/agent-relay-state.d.ts.map +1 -0
  27. package/dist/agent-relay-state.js +58 -0
  28. package/dist/agent-relay-state.js.map +1 -0
  29. package/dist/agent-relay-transport.d.ts +31 -0
  30. package/dist/agent-relay-transport.d.ts.map +1 -0
  31. package/dist/agent-relay-transport.js +199 -0
  32. package/dist/agent-relay-transport.js.map +1 -0
  33. package/dist/authored-budget.d.ts +13 -0
  34. package/dist/authored-budget.d.ts.map +1 -0
  35. package/dist/authored-budget.js +78 -0
  36. package/dist/authored-budget.js.map +1 -0
  37. package/dist/authored-flow-error.d.ts +2 -21
  38. package/dist/authored-flow-error.d.ts.map +1 -1
  39. package/dist/authored-flow-error.js +1 -12
  40. package/dist/authored-flow-error.js.map +1 -1
  41. package/dist/authored-flow-executor.d.ts +13 -7
  42. package/dist/authored-flow-executor.d.ts.map +1 -1
  43. package/dist/authored-flow-executor.js +116 -235
  44. package/dist/authored-flow-executor.js.map +1 -1
  45. package/dist/authored-flow-loader.d.ts +10 -1
  46. package/dist/authored-flow-loader.d.ts.map +1 -1
  47. package/dist/authored-flow-loader.js +56 -3
  48. package/dist/authored-flow-loader.js.map +1 -1
  49. package/dist/authored-flow-operation.d.ts +9 -1
  50. package/dist/authored-flow-operation.d.ts.map +1 -1
  51. package/dist/authored-flow-operation.js +35 -4
  52. package/dist/authored-flow-operation.js.map +1 -1
  53. package/dist/authored-helper-effect.d.ts +12 -0
  54. package/dist/authored-helper-effect.d.ts.map +1 -0
  55. package/dist/authored-helper-effect.js +144 -0
  56. package/dist/authored-helper-effect.js.map +1 -0
  57. package/dist/authored-mcp.d.ts +17 -0
  58. package/dist/authored-mcp.d.ts.map +1 -0
  59. package/dist/authored-mcp.js +171 -0
  60. package/dist/authored-mcp.js.map +1 -0
  61. package/dist/authored-memory.d.ts +7 -0
  62. package/dist/authored-memory.d.ts.map +1 -0
  63. package/dist/authored-memory.js +60 -0
  64. package/dist/authored-memory.js.map +1 -0
  65. package/dist/authored-plugin-effect.d.ts +16 -0
  66. package/dist/authored-plugin-effect.d.ts.map +1 -0
  67. package/dist/authored-plugin-effect.js +163 -0
  68. package/dist/authored-plugin-effect.js.map +1 -0
  69. package/dist/authored-step-output.d.ts +23 -0
  70. package/dist/authored-step-output.d.ts.map +1 -0
  71. package/dist/authored-step-output.js +67 -0
  72. package/dist/authored-step-output.js.map +1 -0
  73. package/dist/authored-worker-step.d.ts +15 -0
  74. package/dist/authored-worker-step.d.ts.map +1 -0
  75. package/dist/authored-worker-step.js +136 -0
  76. package/dist/authored-worker-step.js.map +1 -0
  77. package/dist/budget-preflight.d.ts +5 -0
  78. package/dist/budget-preflight.d.ts.map +1 -0
  79. package/dist/budget-preflight.js +33 -0
  80. package/dist/budget-preflight.js.map +1 -0
  81. package/dist/budget.d.ts +14 -0
  82. package/dist/budget.d.ts.map +1 -0
  83. package/dist/budget.js +52 -0
  84. package/dist/budget.js.map +1 -0
  85. package/dist/bundle-transport.d.ts +18 -0
  86. package/dist/bundle-transport.d.ts.map +1 -0
  87. package/dist/bundle-transport.js +111 -0
  88. package/dist/bundle-transport.js.map +1 -0
  89. package/dist/bundle-typescript.d.ts +8 -0
  90. package/dist/bundle-typescript.d.ts.map +1 -0
  91. package/dist/bundle-typescript.js +108 -0
  92. package/dist/bundle-typescript.js.map +1 -0
  93. package/dist/bundle.d.ts +25 -0
  94. package/dist/bundle.d.ts.map +1 -0
  95. package/dist/bundle.js +199 -0
  96. package/dist/bundle.js.map +1 -0
  97. package/dist/child-stop.d.ts +59 -0
  98. package/dist/child-stop.d.ts.map +1 -0
  99. package/dist/child-stop.js +94 -0
  100. package/dist/child-stop.js.map +1 -0
  101. package/dist/cli/add.d.ts +6 -0
  102. package/dist/cli/add.d.ts.map +1 -0
  103. package/dist/cli/add.js +47 -0
  104. package/dist/cli/add.js.map +1 -0
  105. package/dist/cli/build.d.ts +13 -0
  106. package/dist/cli/build.d.ts.map +1 -0
  107. package/dist/cli/build.js +224 -0
  108. package/dist/cli/build.js.map +1 -0
  109. package/dist/cli/bundle-preflight.d.ts +5 -0
  110. package/dist/cli/bundle-preflight.d.ts.map +1 -0
  111. package/dist/cli/bundle-preflight.js +28 -0
  112. package/dist/cli/bundle-preflight.js.map +1 -0
  113. package/dist/cli/check-helper-body.d.ts +4 -0
  114. package/dist/cli/check-helper-body.d.ts.map +1 -0
  115. package/dist/cli/check-helper-body.js +15 -0
  116. package/dist/cli/check-helper-body.js.map +1 -0
  117. package/dist/cli/check-triggers.d.ts +14 -0
  118. package/dist/cli/check-triggers.d.ts.map +1 -0
  119. package/dist/cli/check-triggers.js +37 -0
  120. package/dist/cli/check-triggers.js.map +1 -0
  121. package/dist/cli/check-typescript.d.ts +20 -0
  122. package/dist/cli/check-typescript.d.ts.map +1 -0
  123. package/dist/cli/check-typescript.js +54 -0
  124. package/dist/cli/check-typescript.js.map +1 -0
  125. package/dist/cli/check.d.ts +36 -3
  126. package/dist/cli/check.d.ts.map +1 -1
  127. package/dist/cli/check.js +122 -11
  128. package/dist/cli/check.js.map +1 -1
  129. package/dist/cli/cloud-run.d.ts +8 -0
  130. package/dist/cli/cloud-run.d.ts.map +1 -0
  131. package/dist/cli/cloud-run.js +50 -0
  132. package/dist/cli/cloud-run.js.map +1 -0
  133. package/dist/cli/deploy.d.ts +9 -0
  134. package/dist/cli/deploy.d.ts.map +1 -0
  135. package/dist/cli/deploy.js +58 -0
  136. package/dist/cli/deploy.js.map +1 -0
  137. package/dist/cli/deterministic-failure.d.ts +5 -0
  138. package/dist/cli/deterministic-failure.d.ts.map +1 -0
  139. package/dist/cli/deterministic-failure.js +66 -0
  140. package/dist/cli/deterministic-failure.js.map +1 -0
  141. package/dist/cli/direct-run.d.ts.map +1 -1
  142. package/dist/cli/direct-run.js +83 -18
  143. package/dist/cli/direct-run.js.map +1 -1
  144. package/dist/cli/hn-monitor.d.ts.map +1 -1
  145. package/dist/cli/hn-monitor.js +2 -1
  146. package/dist/cli/hn-monitor.js.map +1 -1
  147. package/dist/cli/replay.d.ts +12 -0
  148. package/dist/cli/replay.d.ts.map +1 -0
  149. package/dist/cli/replay.js +82 -0
  150. package/dist/cli/replay.js.map +1 -0
  151. package/dist/cli/reuse.d.ts +8 -0
  152. package/dist/cli/reuse.d.ts.map +1 -0
  153. package/dist/cli/reuse.js +25 -0
  154. package/dist/cli/reuse.js.map +1 -0
  155. package/dist/cli/run-digest.d.ts +4 -0
  156. package/dist/cli/run-digest.d.ts.map +1 -0
  157. package/dist/cli/run-digest.js +24 -0
  158. package/dist/cli/run-digest.js.map +1 -0
  159. package/dist/cli/run.d.ts +16 -5
  160. package/dist/cli/run.d.ts.map +1 -1
  161. package/dist/cli/run.js +99 -17
  162. package/dist/cli/run.js.map +1 -1
  163. package/dist/cli/serve-webhook.d.ts +40 -0
  164. package/dist/cli/serve-webhook.d.ts.map +1 -0
  165. package/dist/cli/serve-webhook.js +233 -0
  166. package/dist/cli/serve-webhook.js.map +1 -0
  167. package/dist/cli/tick-runner.d.ts.map +1 -1
  168. package/dist/cli/tick-runner.js +2 -1
  169. package/dist/cli/tick-runner.js.map +1 -1
  170. package/dist/cli-adapter.d.ts +8 -14
  171. package/dist/cli-adapter.d.ts.map +1 -1
  172. package/dist/cli-adapter.js +19 -64
  173. package/dist/cli-adapter.js.map +1 -1
  174. package/dist/cli-watch.d.ts +13 -0
  175. package/dist/cli-watch.d.ts.map +1 -0
  176. package/dist/cli-watch.js +185 -0
  177. package/dist/cli-watch.js.map +1 -0
  178. package/dist/cli.d.ts +4 -0
  179. package/dist/cli.d.ts.map +1 -1
  180. package/dist/cli.js +387 -14
  181. package/dist/cli.js.map +1 -1
  182. package/dist/cloud-http.d.ts +22 -0
  183. package/dist/cloud-http.d.ts.map +1 -0
  184. package/dist/cloud-http.js +84 -0
  185. package/dist/cloud-http.js.map +1 -0
  186. package/dist/cloud-run.d.ts +38 -0
  187. package/dist/cloud-run.d.ts.map +1 -0
  188. package/dist/cloud-run.js +112 -0
  189. package/dist/cloud-run.js.map +1 -0
  190. package/dist/compile.d.ts +21 -3
  191. package/dist/compile.d.ts.map +1 -1
  192. package/dist/compile.js +97 -23
  193. package/dist/compile.js.map +1 -1
  194. package/dist/create-flow.d.ts +17 -0
  195. package/dist/create-flow.d.ts.map +1 -0
  196. package/dist/create-flow.js +72 -0
  197. package/dist/create-flow.js.map +1 -0
  198. package/dist/daemon-connection.d.ts +14 -0
  199. package/dist/daemon-connection.d.ts.map +1 -1
  200. package/dist/daemon-connection.js +33 -1
  201. package/dist/daemon-connection.js.map +1 -1
  202. package/dist/demo-hn-monitor.js +2 -1
  203. package/dist/demo-hn-monitor.js.map +1 -1
  204. package/dist/effect-channel.d.ts +30 -0
  205. package/dist/effect-channel.d.ts.map +1 -0
  206. package/dist/effect-channel.js +71 -0
  207. package/dist/effect-channel.js.map +1 -0
  208. package/dist/failure-kinds.d.ts +14 -3
  209. package/dist/failure-kinds.d.ts.map +1 -1
  210. package/dist/failure-kinds.js +27 -0
  211. package/dist/failure-kinds.js.map +1 -1
  212. package/dist/gate-contract.d.ts.map +1 -1
  213. package/dist/gate-contract.js +9 -0
  214. package/dist/gate-contract.js.map +1 -1
  215. package/dist/helper-preflight.d.ts +18 -0
  216. package/dist/helper-preflight.d.ts.map +1 -0
  217. package/dist/helper-preflight.js +32 -0
  218. package/dist/helper-preflight.js.map +1 -0
  219. package/dist/helper-storage.d.ts +5 -0
  220. package/dist/helper-storage.d.ts.map +1 -0
  221. package/dist/helper-storage.js +31 -0
  222. package/dist/helper-storage.js.map +1 -0
  223. package/dist/helper-writeback.d.ts +12 -0
  224. package/dist/helper-writeback.d.ts.map +1 -0
  225. package/dist/helper-writeback.js +95 -0
  226. package/dist/helper-writeback.js.map +1 -0
  227. package/dist/index.d.ts +10 -2
  228. package/dist/index.d.ts.map +1 -1
  229. package/dist/index.js +7 -1
  230. package/dist/index.js.map +1 -1
  231. package/dist/input-binding.d.ts +4 -0
  232. package/dist/input-binding.d.ts.map +1 -0
  233. package/dist/input-binding.js +78 -0
  234. package/dist/input-binding.js.map +1 -0
  235. package/dist/journal-client.d.ts +12 -4
  236. package/dist/journal-client.d.ts.map +1 -1
  237. package/dist/journal-client.js +17 -6
  238. package/dist/journal-client.js.map +1 -1
  239. package/dist/journal-offset.d.ts +3 -0
  240. package/dist/journal-offset.d.ts.map +1 -0
  241. package/dist/journal-offset.js +69 -0
  242. package/dist/journal-offset.js.map +1 -0
  243. package/dist/journal-reader.d.ts +30 -0
  244. package/dist/journal-reader.d.ts.map +1 -0
  245. package/dist/journal-reader.js +156 -0
  246. package/dist/journal-reader.js.map +1 -0
  247. package/dist/json-schema.d.ts +2 -0
  248. package/dist/json-schema.d.ts.map +1 -1
  249. package/dist/json-schema.js +38 -22
  250. package/dist/json-schema.js.map +1 -1
  251. package/dist/llm-worker.d.ts +16 -0
  252. package/dist/llm-worker.d.ts.map +1 -0
  253. package/dist/llm-worker.js +79 -0
  254. package/dist/llm-worker.js.map +1 -0
  255. package/dist/local-agent.d.ts +8 -0
  256. package/dist/local-agent.d.ts.map +1 -0
  257. package/dist/local-agent.js +25 -0
  258. package/dist/local-agent.js.map +1 -0
  259. package/dist/mcp-client.d.ts +15 -0
  260. package/dist/mcp-client.d.ts.map +1 -0
  261. package/dist/mcp-client.js +104 -0
  262. package/dist/mcp-client.js.map +1 -0
  263. package/dist/mcp-config.d.ts +4 -0
  264. package/dist/mcp-config.d.ts.map +1 -0
  265. package/dist/mcp-config.js +61 -0
  266. package/dist/mcp-config.js.map +1 -0
  267. package/dist/mcp-stdio.d.ts +24 -0
  268. package/dist/mcp-stdio.d.ts.map +1 -0
  269. package/dist/mcp-stdio.js +89 -0
  270. package/dist/mcp-stdio.js.map +1 -0
  271. package/dist/model-pricing.d.ts +22 -0
  272. package/dist/model-pricing.d.ts.map +1 -0
  273. package/dist/model-pricing.js +30 -0
  274. package/dist/model-pricing.js.map +1 -0
  275. package/dist/mount-registry.d.ts +9 -0
  276. package/dist/mount-registry.d.ts.map +1 -0
  277. package/dist/mount-registry.js +47 -0
  278. package/dist/mount-registry.js.map +1 -0
  279. package/dist/named-gate-lowering.d.ts +9 -0
  280. package/dist/named-gate-lowering.d.ts.map +1 -0
  281. package/dist/named-gate-lowering.js +152 -0
  282. package/dist/named-gate-lowering.js.map +1 -0
  283. package/dist/named-gates.d.ts +10 -0
  284. package/dist/named-gates.d.ts.map +1 -0
  285. package/dist/named-gates.js +69 -0
  286. package/dist/named-gates.js.map +1 -0
  287. package/dist/observer-link.d.ts +129 -0
  288. package/dist/observer-link.d.ts.map +1 -0
  289. package/dist/observer-link.js +274 -0
  290. package/dist/observer-link.js.map +1 -0
  291. package/dist/plugin-loader.d.ts +13 -0
  292. package/dist/plugin-loader.d.ts.map +1 -0
  293. package/dist/plugin-loader.js +110 -0
  294. package/dist/plugin-loader.js.map +1 -0
  295. package/dist/plugin-manifest.d.ts +27 -0
  296. package/dist/plugin-manifest.d.ts.map +1 -0
  297. package/dist/plugin-manifest.js +89 -0
  298. package/dist/plugin-manifest.js.map +1 -0
  299. package/dist/preflight.d.ts +26 -2
  300. package/dist/preflight.d.ts.map +1 -1
  301. package/dist/preflight.js +175 -1
  302. package/dist/preflight.js.map +1 -1
  303. package/dist/progress.d.ts +14 -0
  304. package/dist/progress.d.ts.map +1 -0
  305. package/dist/progress.js +39 -0
  306. package/dist/progress.js.map +1 -0
  307. package/dist/protocol.d.ts +11 -0
  308. package/dist/protocol.d.ts.map +1 -1
  309. package/dist/pty-sidechannel.d.ts +14 -0
  310. package/dist/pty-sidechannel.d.ts.map +1 -0
  311. package/dist/pty-sidechannel.js +103 -0
  312. package/dist/pty-sidechannel.js.map +1 -0
  313. package/dist/scope-compiler.d.ts +52 -0
  314. package/dist/scope-compiler.d.ts.map +1 -0
  315. package/dist/scope-compiler.js +62 -0
  316. package/dist/scope-compiler.js.map +1 -0
  317. package/dist/slack-preflight.d.ts +6 -0
  318. package/dist/slack-preflight.d.ts.map +1 -0
  319. package/dist/slack-preflight.js +28 -0
  320. package/dist/slack-preflight.js.map +1 -0
  321. package/dist/slack-writeback.d.ts +40 -0
  322. package/dist/slack-writeback.d.ts.map +1 -0
  323. package/dist/slack-writeback.js +60 -0
  324. package/dist/slack-writeback.js.map +1 -0
  325. package/dist/spec.d.ts +134 -4
  326. package/dist/spec.d.ts.map +1 -1
  327. package/dist/spec.js.map +1 -1
  328. package/dist/step-dependencies.d.ts.map +1 -1
  329. package/dist/step-dependencies.js +2 -1
  330. package/dist/step-dependencies.js.map +1 -1
  331. package/dist/step-fields.d.ts +4 -4
  332. package/dist/step-fields.d.ts.map +1 -1
  333. package/dist/step-fields.js +5 -2
  334. package/dist/step-fields.js.map +1 -1
  335. package/dist/trigger-executor.d.ts +7 -0
  336. package/dist/trigger-executor.d.ts.map +1 -0
  337. package/dist/trigger-executor.js +35 -0
  338. package/dist/trigger-executor.js.map +1 -0
  339. package/dist/validate.d.ts.map +1 -1
  340. package/dist/validate.js +53 -6
  341. package/dist/validate.js.map +1 -1
  342. package/dist/webhook-rate-limit.d.ts +29 -0
  343. package/dist/webhook-rate-limit.d.ts.map +1 -0
  344. package/dist/webhook-rate-limit.js +38 -0
  345. package/dist/webhook-rate-limit.js.map +1 -0
  346. package/dist/webhook-signature.d.ts +22 -0
  347. package/dist/webhook-signature.d.ts.map +1 -0
  348. package/dist/webhook-signature.js +53 -0
  349. package/dist/webhook-signature.js.map +1 -0
  350. package/dist/worker-cli.d.ts +16 -1
  351. package/dist/worker-cli.d.ts.map +1 -1
  352. package/dist/worker-cli.js +129 -12
  353. package/dist/worker-cli.js.map +1 -1
  354. package/dist/worker-input.d.ts +4 -0
  355. package/dist/worker-input.d.ts.map +1 -0
  356. package/dist/worker-input.js +14 -0
  357. package/dist/worker-input.js.map +1 -0
  358. package/dist/worker-lease.d.ts +5 -0
  359. package/dist/worker-lease.d.ts.map +1 -0
  360. package/dist/worker-lease.js +80 -0
  361. package/dist/worker-lease.js.map +1 -0
  362. package/dist/worker-spend.d.ts +19 -0
  363. package/dist/worker-spend.d.ts.map +1 -0
  364. package/dist/worker-spend.js +22 -0
  365. package/dist/worker-spend.js.map +1 -0
  366. package/dist/worker-usage.d.ts +7 -0
  367. package/dist/worker-usage.d.ts.map +1 -0
  368. package/dist/worker-usage.js +54 -0
  369. package/dist/worker-usage.js.map +1 -0
  370. package/dist/worker.d.ts +2 -0
  371. package/dist/worker.d.ts.map +1 -1
  372. package/dist/worker.js +29 -9
  373. package/dist/worker.js.map +1 -1
  374. package/dist/wrapper-session.d.ts +1 -1
  375. package/dist/wrapper-session.d.ts.map +1 -1
  376. package/dist/wrapper-session.js +56 -19
  377. package/dist/wrapper-session.js.map +1 -1
  378. package/dist/yaml-helper-effect.d.ts +8 -0
  379. package/dist/yaml-helper-effect.d.ts.map +1 -0
  380. package/dist/yaml-helper-effect.js +79 -0
  381. package/dist/yaml-helper-effect.js.map +1 -0
  382. package/dist/yaml-helpers.d.ts +18 -0
  383. package/dist/yaml-helpers.d.ts.map +1 -0
  384. package/dist/yaml-helpers.js +116 -0
  385. package/dist/yaml-helpers.js.map +1 -0
  386. package/package.json +19 -2
  387. package/src/adapters/base.ts +53 -0
  388. package/src/adapters/claude.ts +55 -0
  389. package/src/adapters/codex.ts +56 -0
  390. package/src/adapters/index.ts +36 -0
  391. package/src/adapters/wrapper.ts +42 -0
  392. package/src/agent-relay-receipt.ts +120 -0
  393. package/src/agent-relay-state.ts +73 -0
  394. package/src/agent-relay-transport.ts +273 -0
  395. package/src/authored-budget.ts +69 -0
  396. package/src/authored-flow-error.ts +10 -10
  397. package/src/authored-flow-executor.ts +171 -347
  398. package/src/authored-flow-loader.ts +60 -3
  399. package/src/authored-flow-operation.ts +38 -5
  400. package/src/authored-helper-effect.ts +143 -0
  401. package/src/authored-mcp.ts +166 -0
  402. package/src/authored-memory.ts +55 -0
  403. package/src/authored-plugin-effect.ts +152 -0
  404. package/src/authored-step-output.ts +107 -0
  405. package/src/authored-worker-step.ts +181 -0
  406. package/src/budget-preflight.ts +31 -0
  407. package/src/budget.ts +49 -0
  408. package/src/bundle-transport.ts +79 -0
  409. package/src/bundle-typescript.ts +98 -0
  410. package/src/bundle.ts +182 -0
  411. package/src/child-stop.ts +138 -0
  412. package/src/cli/add.ts +45 -0
  413. package/src/cli/build.ts +214 -0
  414. package/src/cli/bundle-preflight.ts +28 -0
  415. package/src/cli/check-helper-body.ts +14 -0
  416. package/src/cli/check-triggers.ts +41 -0
  417. package/src/cli/check-typescript.ts +63 -0
  418. package/src/cli/check.ts +132 -14
  419. package/src/cli/cloud-run.ts +47 -0
  420. package/src/cli/deploy.ts +52 -0
  421. package/src/cli/deterministic-failure.ts +69 -0
  422. package/src/cli/direct-run.ts +79 -18
  423. package/src/cli/hn-monitor.ts +2 -1
  424. package/src/cli/replay.ts +81 -0
  425. package/src/cli/reuse.ts +23 -0
  426. package/src/cli/run-digest.ts +24 -0
  427. package/src/cli/run.ts +104 -21
  428. package/src/cli/serve-webhook.ts +246 -0
  429. package/src/cli/tick-runner.ts +2 -1
  430. package/src/cli-adapter.ts +31 -74
  431. package/src/cli-watch.ts +174 -0
  432. package/src/cli.ts +396 -17
  433. package/src/cloud-http.ts +100 -0
  434. package/src/cloud-run.ts +137 -0
  435. package/src/compile.ts +102 -23
  436. package/src/create-flow.ts +82 -0
  437. package/src/daemon-connection.ts +32 -1
  438. package/src/demo-hn-monitor.ts +2 -1
  439. package/src/effect-channel.ts +97 -0
  440. package/src/failure-kinds.ts +38 -0
  441. package/src/gate-contract.ts +7 -0
  442. package/src/helper-preflight.ts +34 -0
  443. package/src/helper-storage.ts +23 -0
  444. package/src/helper-writeback.ts +83 -0
  445. package/src/index.ts +26 -0
  446. package/src/input-binding.ts +76 -0
  447. package/src/journal-client.ts +20 -7
  448. package/src/journal-offset.ts +60 -0
  449. package/src/journal-reader.ts +165 -0
  450. package/src/json-schema.ts +34 -18
  451. package/src/llm-worker.ts +82 -0
  452. package/src/local-agent.ts +30 -0
  453. package/src/mcp-client.ts +97 -0
  454. package/src/mcp-config.ts +42 -0
  455. package/src/mcp-stdio.ts +84 -0
  456. package/src/model-pricing.ts +32 -0
  457. package/src/mount-registry.ts +44 -0
  458. package/src/named-gate-lowering.ts +147 -0
  459. package/src/named-gates.ts +76 -0
  460. package/src/observer-link.ts +368 -0
  461. package/src/plugin-loader.ts +88 -0
  462. package/src/plugin-manifest.ts +76 -0
  463. package/src/preflight.ts +178 -4
  464. package/src/progress.ts +51 -0
  465. package/src/protocol.ts +12 -0
  466. package/src/pty-sidechannel.ts +92 -0
  467. package/src/scope-compiler.ts +105 -0
  468. package/src/slack-preflight.ts +25 -0
  469. package/src/slack-writeback.ts +63 -0
  470. package/src/spec.ts +112 -4
  471. package/src/step-dependencies.ts +3 -1
  472. package/src/step-fields.ts +5 -2
  473. package/src/trigger-executor.ts +34 -0
  474. package/src/validate.ts +47 -6
  475. package/src/webhook-rate-limit.ts +69 -0
  476. package/src/webhook-signature.ts +74 -0
  477. package/src/worker-cli.ts +150 -11
  478. package/src/worker-input.ts +15 -0
  479. package/src/worker-lease.ts +85 -0
  480. package/src/worker-spend.ts +23 -0
  481. package/src/worker-usage.ts +48 -0
  482. package/src/worker.ts +34 -10
  483. package/src/wrapper-session.ts +50 -15
  484. package/src/yaml-helper-effect.ts +78 -0
  485. package/src/yaml-helpers.ts +131 -0
@@ -0,0 +1,214 @@
1
+ import { readFile, lstat } from 'node:fs/promises';
2
+ import { dirname, extname, join, resolve } from 'node:path';
3
+ import { parse } from 'yaml';
4
+ import { sealBundle, verifyBundle, type BundleFile } from '../bundle.js';
5
+ import { canonicalize } from '../canonical.js';
6
+ import { compileSpec, toKernelSpec } from '../compile.js';
7
+ import { preflight } from '../preflight.js';
8
+ import { buildTypescript } from '../bundle-typescript.js';
9
+ import { checkBuildableFlow, readProjectConfig, type CheckReport } from './check.js';
10
+ import type { CliIo } from '../cli.js';
11
+ import type { FlowSpec } from '../spec.js';
12
+
13
+ export interface BuildArgs { command: 'build'; value: string; out?: string; verify: boolean; json: boolean }
14
+
15
+ export function parseBuildArgs(args: readonly string[]): BuildArgs | undefined {
16
+ if (args[0] === '--verify') {
17
+ return args.length === 2 && !args[1]!.startsWith('-')
18
+ ? { command: 'build', value: args[1]!, verify: true, json: false } : undefined;
19
+ }
20
+ let out: string | undefined;
21
+ let value: string | undefined;
22
+ let json = false;
23
+ for (let i = 0; i < args.length; i++) {
24
+ const arg = args[i]!;
25
+ if (arg === '--out') {
26
+ if (out !== undefined || args[i + 1] === undefined || args[i + 1]!.startsWith('-')) return undefined;
27
+ out = args[++i];
28
+ } else if (arg === '--json') {
29
+ if (json) return undefined;
30
+ json = true;
31
+ } else if (arg.startsWith('-') || value !== undefined) return undefined;
32
+ else value = arg;
33
+ }
34
+ return value === undefined ? undefined : { command: 'build', value, out, verify: false, json };
35
+ }
36
+
37
+ /**
38
+ * Emit a check report in the same shape `flows check` uses so build-time
39
+ * refusals are consumable by the same tooling.
40
+ */
41
+ function emitBuildCheckReport(report: CheckReport, json: boolean, io: CliIo): void {
42
+ for (const diagnostic of report.diagnostics) {
43
+ if (diagnostic.severity !== 'refusal') continue;
44
+ io.stderr(`REFUSED [${diagnostic.kind}] ${diagnostic.message}`);
45
+ }
46
+ if (json) io.stdout(JSON.stringify(report));
47
+ }
48
+
49
+ export async function runBuild(args: BuildArgs, io: CliIo): Promise<0 | 2> {
50
+ try {
51
+ if (args.verify) {
52
+ io.stdout(`VERIFIED sha256:${await verifyBundle(args.value)}`);
53
+ return 0;
54
+ }
55
+ // Gate the build on the same preflight pipeline `flows check` uses.
56
+ // Refusals never leave partial artifacts — no file capture, no canonical
57
+ // spec write, no digest computation, no bundle directory creation. A
58
+ // previous `dist/flows/<name>@sha256:<hex>/` directory from an earlier
59
+ // successful build is not touched (buildFlow is never called).
60
+ const gate = await checkBuildableFlow(args.value);
61
+ if (!gate.report.ok) {
62
+ emitBuildCheckReport(gate.report, args.json, io);
63
+ return 2;
64
+ }
65
+ io.stdout(await buildFlow(args.value, args.out ?? 'dist/flows', io.stderr));
66
+ return 0;
67
+ } catch (error) {
68
+ io.stderr(`REFUSED [bundle_invalid] ${error instanceof Error ? error.message : String(error)}`);
69
+ return 2;
70
+ }
71
+ }
72
+
73
+ /** Build checks never probe credentials, workers, or the author's PATH. */
74
+ export async function buildFlow(path: string, out: string, warn: (line: string) => void): Promise<string> {
75
+ const input = resolve(path);
76
+ const directory = dirname(input);
77
+ const files: BundleFile[] = [];
78
+ let authoring: FlowSpec;
79
+ let authored = false;
80
+ let compiler: string | undefined;
81
+ if (extname(input) === '.ts') {
82
+ const result = await buildTypescript(input);
83
+ authoring = result.spec;
84
+ authored = result.authored;
85
+ compiler = result.compiler;
86
+ files.push(...result.files);
87
+ } else if (['.yaml', '.yml'].includes(extname(input))) {
88
+ authoring = compileSpec(parse(await readFile(input, 'utf8')));
89
+ files.push({ path: 'lockfile.json', data: canonicalize({ version: 1, adapters: [] }) });
90
+ } else throw new Error('build expects a .yaml, .yml, or .ts flow');
91
+
92
+ // The current preflight API reports uncollected environment facts as
93
+ // probe_failed. Preserve that truthful report verbatim and separately declare
94
+ // the deployment obligations; never manufacture successful auth probes.
95
+ //
96
+ // Load the nearest flows.json so declared model allowlists and the project
97
+ // CLI participate in build-time preflight. Without them a declared `model:`
98
+ // that appears in flows.json/models refuses the whole build as
99
+ // `model_unknown`, because preflight defaults treat "no registry" as "no
100
+ // model is known" and only environment probes (deferred here) would rescue
101
+ // it. Model existence is a build-provable fact; only the live probe is not.
102
+ const config = readProjectConfig(directory);
103
+ const report = preflight(authoring, {
104
+ ...(config.cli !== undefined ? { projectCli: config.cli } : {}),
105
+ ...(config.path !== undefined ? { projectConfigPath: config.path } : {}),
106
+ projectSearchStart: directory,
107
+ models: config.models,
108
+ ...(config.path !== undefined ? { modelRegistryPath: config.path } : {}),
109
+ probes: {
110
+ cli: () => { throw new Error('deferred to deployment'); },
111
+ executor: () => { throw new Error('deferred to deployment'); },
112
+ command: () => { throw new Error('deferred to deployment'); },
113
+ },
114
+ });
115
+ const refusals = report.diagnostics.filter(d => d.severity === 'refusal' && d.kind !== 'probe_failed');
116
+ if (refusals.length > 0) throw new Error(refusals.map(d => `[${d.kind}] ${d.message}`).join('\n'));
117
+ authoring = await captureFiles(authoring, directory, files);
118
+ files.push(
119
+ { path: 'spec.canonical.json', data: canonicalize(toKernelSpec(authoring)) },
120
+ { path: 'preflight.json', data: canonicalize(report) },
121
+ { path: 'metadata.json', data: canonicalize({
122
+ version: 1, platform: { os: process.platform, arch: process.arch },
123
+ executables: files.filter(file => file.path === 'flow' || file.executable).map(file => file.path).sort(),
124
+ kind: authored ? 'authored-typescript' : 'declarative',
125
+ ...(compiler === undefined ? {} : { compiler }),
126
+ preflight: { buildPassed: true, deferred: ['commands', 'credentials', 'workers', 'mcp_servers'],
127
+ ...(authored ? { dynamicSteps: 'exported spec declaration checked; body was not executed during build' } : {}) },
128
+ }) },
129
+ );
130
+ return sealBundle({ name: authoring.name ?? 'flow', out, files,
131
+ repo: await repositoryRoot(directory), warn });
132
+ }
133
+
134
+ async function repositoryRoot(start: string): Promise<string> {
135
+ for (let dir = start; ; dir = dirname(dir)) {
136
+ try { await lstat(join(dir, '.git')); return dir; }
137
+ catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
138
+ if (dirname(dir) === dir) return start;
139
+ }
140
+ }
141
+
142
+ /** Relative CLI executables are the file references in the current spec schema.
143
+ * Shell commands are opaque; explicit ./file words are captured without
144
+ * interpreting substitutions or running a shell at build time. */
145
+ async function captureFiles(flow: FlowSpec, directory: string, files: BundleFile[]): Promise<FlowSpec> {
146
+ // Cache the in-flight *promise*, not the resolved string. Two concurrent
147
+ // captures of the same path (Promise.all over named agents that share a CLI)
148
+ // both pass a `captured.has(path)` guard before either has finished awaiting
149
+ // its lstat/readFile, so both would push the same `assets/...` entry -- and
150
+ // sealBundle then refuses the whole flow as a duplicate. Storing the promise
151
+ // dedups on the first synchronous look-up.
152
+ const captured = new Map<string, Promise<string>>();
153
+ function capture(path: string): Promise<string> {
154
+ if (!path.includes('/')) return Promise.resolve(path);
155
+ if (!path.startsWith('./') || path.split('/').includes('..') || path.includes('\\')) {
156
+ return Promise.reject(new Error(`${path}: bundle file references must start with ./ and stay inside the flow directory`));
157
+ }
158
+ const existing = captured.get(path);
159
+ if (existing !== undefined) return existing;
160
+ const pending = (async () => {
161
+ const parts = path.slice(2).split('/');
162
+ if (parts.some(p => p === '' || p === '.')) throw new Error(`${path}: invalid file reference`);
163
+ let executable = false;
164
+ for (let i = 1; i <= parts.length; i++) {
165
+ const stat = await lstat(join(directory, ...parts.slice(0, i)));
166
+ if (i === parts.length ? !stat.isFile() : !stat.isDirectory()) throw new Error(`${path}: expected regular file without symlinks`);
167
+ if (i === parts.length) executable = (stat.mode & 0o111) !== 0;
168
+ }
169
+ const target = `assets/${parts.join('/')}`;
170
+ files.push({ path: target, data: await readFile(join(directory, ...parts)), executable });
171
+ return `./${target}`;
172
+ })();
173
+ captured.set(path, pending);
174
+ return pending;
175
+ }
176
+ // Capture the flow-level `cli` default and every named-agent `cli` BEFORE
177
+ // walking steps. Otherwise a step that inherits its CLI from the flow header
178
+ // or an agents-map entry never emits its own `cli`, so the step-level walk
179
+ // below misses it, the executable never enters the bundle, and the sealed
180
+ // spec still points at the author's absolute path outside the bundle.
181
+ const flowCli = flow.cli !== undefined ? await capture(flow.cli) : undefined;
182
+ const agents: FlowSpec['agents'] = flow.agents === undefined ? undefined
183
+ : Object.fromEntries(await Promise.all(Object.entries(flow.agents).map(
184
+ async ([name, agent]) => [name, { ...agent, cli: await capture(agent.cli) }] as const,
185
+ )));
186
+ const compiled = toKernelSpec(flow);
187
+ // Lower named/flow CLI resolution first so every runtime reference is bound.
188
+ const steps: FlowSpec['steps'] = [];
189
+ for (const [index, step] of flow.steps.entries()) {
190
+ if (step.type !== 'deterministic') {
191
+ const lowered = compiled.steps[index];
192
+ const cli = lowered && lowered.type !== 'deterministic' ? lowered.cli : undefined;
193
+ steps.push(cli === undefined ? step : { ...step, cli: await capture(cli) });
194
+ continue;
195
+ }
196
+ let command = step.command;
197
+ const references = [...command.matchAll(/(?:^|[\s;|&<>])(?:"(\.\/[^"$`]+)"|'(\.\/[^']+)'|(\.\/[^\s;|&<>"'$`]+))/g)];
198
+ // Replace only each complete matched word, from right to left so offsets
199
+ // stay valid and similarly prefixed filenames cannot rewrite one another.
200
+ for (const match of references.reverse()) {
201
+ const ref = (match[1] ?? match[2] ?? match[3])!;
202
+ const start = match.index! + match[0].indexOf(ref);
203
+ command = command.slice(0, start) + await capture(ref) + command.slice(start + ref.length);
204
+ }
205
+ if (/^\s*["']?\//.test(command)) throw new Error(`${step.id}: absolute command paths cannot be bundled`);
206
+ steps.push({ ...step, command });
207
+ }
208
+ return {
209
+ ...flow,
210
+ ...(flowCli !== undefined ? { cli: flowCli } : {}),
211
+ ...(agents !== undefined ? { agents } : {}),
212
+ steps,
213
+ };
214
+ }
@@ -0,0 +1,28 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { BundleFailure } from '../bundle-transport.js';
4
+ import { compileSpec, kernelToAuthoring } from '../compile.js';
5
+ import { checkAuthoredFlow, type CheckExecution } from './check.js';
6
+
7
+ /** Refuse unsupported execution: a TS declaration is not its body, and
8
+ * daemon-relative assets must never resolve in an unrelated checkout. */
9
+ export async function checkRunnableBundle(directory: string, name: string): Promise<CheckExecution> {
10
+ const metadata = JSON.parse(await readFile(join(directory, 'metadata.json'), 'utf8'));
11
+ const manifest = JSON.parse(await readFile(join(directory, 'manifest.json'), 'utf8')) as { path: string }[];
12
+ const flow = compileSpec(kernelToAuthoring(JSON.parse(await readFile(join(directory, 'spec.canonical.json'), 'utf8'))));
13
+ if (flow.name !== undefined && flow.name !== name) throw new BundleFailure('bundle_signature_invalid', 'Bundle name does not match the requested flow.');
14
+ flow.name ??= name;
15
+ if (metadata.kind !== 'declarative' || flow.steps.some(step => step.type !== 'deterministic'
16
+ || step.requirements !== undefined) || (flow.triggers?.length ?? 0) > 0
17
+ || manifest.some(entry => entry.path.startsWith('assets/'))) {
18
+ throw new BundleFailure('bundle_unsupported',
19
+ 'This slice runs declarative deterministic bundles without assets, placement requirements, or triggers.');
20
+ }
21
+ // Keep existing fail-closed environment checks before transport/journal.
22
+ // Splitting static and environment checks is follow-up.
23
+ // This deterministic-only slice declares no project models or executors.
24
+ // Never discover flows.json in the deployment/cache tree or its ancestors.
25
+ return checkAuthoredFlow(flow, join(directory, 'spec.canonical.json'), {
26
+ directory, models: [], executors: [],
27
+ });
28
+ }
@@ -0,0 +1,14 @@
1
+ import { loadAuthoredFlow } from '../authored-flow-loader.js';
2
+ import { checkSlackHelpers } from '../slack-preflight.js';
3
+ import { inputFailureReport, type CheckExecution } from './check.js';
4
+
5
+ /** Imports the definition, but never executes arbitrary authored body code. */
6
+ export async function checkHelperBody(path: string): Promise<CheckExecution> {
7
+ try {
8
+ const { handle, getDefinition } = await loadAuthoredFlow(path);
9
+ return { report: { ...checkSlackHelpers(getDefinition(handle)), path } };
10
+ } catch (error) {
11
+ return { report: inputFailureReport({ kind: 'invalid_spec',
12
+ message: error instanceof Error ? error.message : 'Could not import authored flow.' }, path) };
13
+ }
14
+ }
@@ -0,0 +1,41 @@
1
+ import { dirname, resolve } from 'node:path';
2
+ import { loadAuthoredFlow, type LoadedAuthoredFlow } from '../authored-flow-loader.js';
3
+ import { preflightWebhookTriggers } from '../preflight.js';
4
+ import { checkSlackHelpers } from '../slack-preflight.js';
5
+ import { inputFailureReport, readProjectConfig, type CheckReport } from './check.js';
6
+
7
+ /**
8
+ * Inspect an authored flow without running a handler or contacting the daemon.
9
+ * Combines the two authored-only preflight paths that share a loaded flow: the
10
+ * webhook-trigger check (E) and the f.slack helper check (B). Skipping either
11
+ * turned this into a silent trapdoor -- a `.flow.ts` using `f.slack.post`
12
+ * without a token would pass `flows check` and only crash at run.
13
+ */
14
+ export async function checkAuthoredTriggers(path: string): Promise<{
15
+ report: CheckReport;
16
+ loaded?: LoadedAuthoredFlow;
17
+ }> {
18
+ try {
19
+ const loaded = await loadAuthoredFlow(path);
20
+ const definition = loaded.getDefinition(loaded.handle);
21
+ const config = readProjectConfig(dirname(resolve(path)));
22
+ const triggerDiagnostics = preflightWebhookTriggers(
23
+ (definition.handlers ?? []).map(handler => handler.trigger), config.executors,
24
+ );
25
+ const helperReport = checkSlackHelpers(definition);
26
+ const diagnostics = [...triggerDiagnostics, ...helperReport.diagnostics];
27
+ return {
28
+ loaded,
29
+ report: {
30
+ ok: diagnostics.length === 0, path, gates: [], resolutions: [], diagnostics,
31
+ ...(config.path === undefined ? {} : { projectConfigPath: config.path }),
32
+ },
33
+ };
34
+ } catch (error) {
35
+ return { report: inputFailureReport({
36
+ kind: typeof error === 'object' && error !== null && 'kind' in error && error.kind === 'config_invalid'
37
+ ? 'config_invalid' : 'invalid_spec',
38
+ message: error instanceof Error ? error.message : 'Cannot inspect authored triggers',
39
+ }, path) };
40
+ }
41
+ }
@@ -0,0 +1,63 @@
1
+ import type { LoadedPlugin } from '../plugin-loader.js';
2
+ import { dirname, resolve } from 'node:path';
3
+ import type { AuthoredFlowDefinition } from '../authored-flow.js';
4
+ import { loadAuthoredFlow } from '../authored-flow-loader.js';
5
+ import { preflight } from '../preflight.js';
6
+ import { SPEC_SCHEMA_VERSION, type McpServerConfig } from '../spec.js';
7
+ import { inputFailureReport, readProjectConfig, type CheckReport } from './check.js';
8
+
9
+ export interface CheckedMcp {
10
+ plugins?: readonly LoadedPlugin[];
11
+ report: CheckReport;
12
+ servers: Readonly<Record<string, McpServerConfig>>;
13
+ inventory: Readonly<Record<string, readonly string[]>>;
14
+ }
15
+
16
+ export async function checkTypeScriptFlow(path: string): Promise<{ report: CheckReport }> {
17
+ try {
18
+ const { handle, getDefinition } = await loadAuthoredFlow(path);
19
+ return await checkMcpHeader(getDefinition(handle), path);
20
+ } catch (error) {
21
+ return { report: inputFailureReport({ kind: 'invalid_spec', message: (error as Error).message }, path) };
22
+ }
23
+ }
24
+
25
+ /** Check declarations without executing the authored body. */
26
+ export async function checkMcpHeader(
27
+ definition: Pick<AuthoredFlowDefinition, 'name' | 'header'>,
28
+ path: string,
29
+ ): Promise<CheckedMcp> {
30
+ const empty = { servers: Object.freeze({}), inventory: Object.freeze({}) };
31
+ const KNOWN_HEADER_FIELDS = new Set(['tools', 'budget', 'identity', 'memory', 'workspace', 'use']);
32
+ const header = definition.header ?? {};
33
+ const unsupported = Object.keys(header).filter(key => !KNOWN_HEADER_FIELDS.has(key));
34
+ if (header.tools?.relayfile !== undefined) unsupported.push('tools.relayfile');
35
+ if (unsupported.length) {
36
+ return { ...empty, report: inputFailureReport({ kind: 'invalid_spec',
37
+ message: `flow "${definition.name}" uses unsupported header fields: ${unsupported.join(', ')}` }, path) };
38
+ }
39
+ try {
40
+ const config = readProjectConfig(dirname(resolve(path)));
41
+ const result = await preflight({ version: SPEC_SCHEMA_VERSION, name: definition.name,
42
+ steps: [{ id: 'header', type: 'deterministic', command: ':' }] }, {
43
+ pluginSearchStart: dirname(resolve(path)),
44
+ mcpServers: header.tools?.mcp ?? [], mcp: config.mcp,
45
+ probes: { command: () => true, cli: () => { throw new Error('no CLI declared'); }, executor: () => false },
46
+ });
47
+ return {
48
+ plugins: result.plugins,
49
+ servers: config.mcp ?? empty.servers,
50
+ inventory: result.mcpTools ?? empty.inventory,
51
+ report: { ...result, path, projectConfigPath: config.path, gates: [],
52
+ diagnostics: result.diagnostics.filter(d => d.stepId !== 'header') },
53
+ };
54
+ } catch (error) {
55
+ return { ...empty, report: inputFailureReport({ kind: 'config_invalid', message: (error as Error).message }, path) };
56
+ }
57
+ }
58
+
59
+ export class McpPreflightError extends Error {
60
+ constructor(readonly report: CheckReport) {
61
+ super(report.diagnostics.map(d => d.message).join('; '));
62
+ }
63
+ }
package/src/cli/check.ts CHANGED
@@ -3,6 +3,7 @@ import { dirname, isAbsolute, join, parse as parsePath, resolve } from 'node:pat
3
3
  import { spawnSync } from 'node:child_process';
4
4
  import { parse as parseYaml } from 'yaml';
5
5
  import { CompileError, compileSpec, kernelToAuthoring } from '../compile.js';
6
+ import { helperReady } from '../yaml-helper-effect.js';
6
7
  import {
7
8
  adapterIdentification,
8
9
  authenticationProbe,
@@ -14,8 +15,10 @@ import {
14
15
  import { MODEL_ENV } from '../worker-cli.js';
15
16
  import { modelNameError } from '../model-name.js';
16
17
  import type { FlowSpec } from '../spec.js';
18
+ import type { McpServerConfig } from '../spec.js';
19
+ import { parseMcpConfig } from '../mcp-config.js';
17
20
  import type { StepGateInspection } from '../gate-contract.js';
18
- import type { CheckFailureKind } from '../failure-kinds.js';
21
+ import type { CheckFailureKind, CheckWarningKind } from '../failure-kinds.js';
19
22
  import {
20
23
  preflight,
21
24
  CliProbeError,
@@ -25,7 +28,9 @@ import {
25
28
  type PreflightProbes,
26
29
  } from '../preflight.js';
27
30
 
28
- interface ProjectConfig {
31
+ export interface ProjectConfig {
32
+ deploy?: { bucket: string };
33
+ mcp?: Record<string, McpServerConfig>;
29
34
  cli?: string;
30
35
  executors: string[];
31
36
  models: string[];
@@ -34,12 +39,19 @@ interface ProjectConfig {
34
39
  }
35
40
 
36
41
  export interface CheckReport {
42
+ mcpTools?: Readonly<Record<string, readonly string[]>>;
37
43
  ok: boolean;
38
44
  path?: string;
39
45
  projectConfigPath?: string;
40
46
  gates: StepGateInspection[];
41
47
  resolutions: CliResolution[];
42
- diagnostics: Array<PreflightDiagnostic | CheckInputDiagnostic>;
48
+ diagnostics: Array<PreflightDiagnostic | CheckInputDiagnostic | CheckWarningDiagnostic>;
49
+ }
50
+
51
+ export interface CheckWarningDiagnostic {
52
+ severity: 'warning';
53
+ kind: CheckWarningKind;
54
+ message: string;
43
55
  }
44
56
 
45
57
  export interface CheckInputDiagnostic {
@@ -53,7 +65,7 @@ export interface CheckExecution {
53
65
  flow?: FlowSpec;
54
66
  }
55
67
 
56
- class CheckFailure extends Error {
68
+ export class CheckFailure extends Error {
57
69
  constructor(readonly kind: CheckFailureKind, message: string) {
58
70
  super(message);
59
71
  }
@@ -63,8 +75,25 @@ class CheckFailure extends Error {
63
75
  export function checkFlow(path: string): CheckExecution {
64
76
  const absolutePath = resolve(path);
65
77
  try {
66
- const flow = readFlow(absolutePath);
67
- return checkAuthoredFlow(flow, path);
78
+ const source = readFlowSource(absolutePath);
79
+ const hint = path.endsWith('.flow.yaml') && !/^\uFEFF?[ \t]*# yaml-language-server:/.test(source)
80
+ ? [{ severity: 'warning' as const, kind: 'editor_schema_missing' as const,
81
+ message: 'For editor validation, add this first line: # yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.json' }]
82
+ : [];
83
+ let execution: CheckExecution;
84
+ try {
85
+ execution = checkAuthoredFlow(readFlow(source, absolutePath), path);
86
+ } catch (error) {
87
+ if (!(error instanceof CheckFailure)) throw error;
88
+ execution = { report: inputFailureReport(error, path) };
89
+ }
90
+ // The editor-schema hint is a documentation nudge, emitted for every
91
+ // .flow.yaml without a first-line yaml-language-server comment.
92
+ // Firing it even when a refusal is present is deliberate: an editor
93
+ // showing squiggles on this file should still tell the author how to
94
+ // wire the schema, so the next edit gets real-time feedback.
95
+ execution.report.diagnostics.push(...hint);
96
+ return execution;
68
97
  } catch (error) {
69
98
  const failure = error instanceof CheckFailure
70
99
  ? error
@@ -74,10 +103,10 @@ export function checkFlow(path: string): CheckExecution {
74
103
  }
75
104
 
76
105
  /** Preflight a validated authored flow through the same path as YAML/JSON. */
77
- export function checkAuthoredFlow(authoring: FlowSpec, path: string): CheckExecution {
106
+ export function checkAuthoredFlow(authoring: FlowSpec, path: string, projectConfig?: ProjectConfig): CheckExecution {
78
107
  const absolutePath = resolve(path);
79
108
  try {
80
- const config = readProjectConfig(dirname(absolutePath));
109
+ const config = projectConfig ?? readProjectConfig(dirname(absolutePath));
81
110
  const probes = systemProbes(dirname(absolutePath), config);
82
111
  const result = preflight(authoring, {
83
112
  projectCli: config.cli,
@@ -114,6 +143,72 @@ export function checkAuthoredFlow(authoring: FlowSpec, path: string): CheckExecu
114
143
  }
115
144
  }
116
145
 
146
+ /**
147
+ * Build-time gate that runs the same preflight pipeline as `checkFlow`
148
+ * but with deferred probes — a build machine is not the deployment target,
149
+ * so `cli`/`command`/`executor` existence is checked at run-time, not here.
150
+ *
151
+ * "Build-provable" refusals (unknown model, `use:` unresolved, invalid
152
+ * verification, missing bundle assets, budget syntax, etc.) still surface,
153
+ * because they are properties of the flow spec, not of the build host.
154
+ */
155
+ export async function checkBuildableFlow(path: string): Promise<CheckExecution> {
156
+ const absolutePath = resolve(path);
157
+ try {
158
+ const authored = /\.(?:[cm]?[jt]s)$/.test(path);
159
+ if (authored) {
160
+ // TS flows are gated by `buildFlow` itself, which runs
161
+ // `buildTypescript` to compile the authored spec and then invokes
162
+ // preflight with deferred probes at the same refusal threshold as
163
+ // this gate. Running `checkTypeScriptFlow` here would need
164
+ // `@relayflows/surface` to be resolvable from the flow's directory,
165
+ // which is not a build-time invariant. Return an ok report so the
166
+ // gate delegates to `buildFlow`'s inline preflight.
167
+ return {
168
+ report: { ok: true, path, gates: [], resolutions: [], diagnostics: [] },
169
+ };
170
+ }
171
+ const source = readFlowSource(absolutePath);
172
+ const authoring: FlowSpec = readFlow(source, absolutePath);
173
+ const config = readProjectConfig(dirname(absolutePath));
174
+ const deferred: PreflightProbes = {
175
+ cli: () => { throw new Error('deferred to deployment'); },
176
+ executor: () => { throw new Error('deferred to deployment'); },
177
+ command: () => { throw new Error('deferred to deployment'); },
178
+ };
179
+ const result = preflight(authoring, {
180
+ ...(config.cli !== undefined ? { projectCli: config.cli } : {}),
181
+ ...(config.path !== undefined ? { projectConfigPath: config.path } : {}),
182
+ projectSearchStart: dirname(absolutePath),
183
+ models: config.models,
184
+ ...(config.path !== undefined ? { modelRegistryPath: config.path } : {}),
185
+ probes: deferred,
186
+ });
187
+ // Refusals rooted in build-machine environment probes (`probe_failed`)
188
+ // are excluded from the build gate — the build host is not the
189
+ // deployment target, and those checks are re-run at `flows run`.
190
+ const diagnostics = result.diagnostics.filter(
191
+ (d) => !(d.severity === 'refusal' && d.kind === 'probe_failed'),
192
+ );
193
+ const ok = !diagnostics.some((d) => d.severity === 'refusal');
194
+ return {
195
+ report: {
196
+ ok,
197
+ path,
198
+ ...(config.path !== undefined ? { projectConfigPath: config.path } : {}),
199
+ gates: result.gates,
200
+ resolutions: result.resolutions,
201
+ diagnostics,
202
+ },
203
+ };
204
+ } catch (error) {
205
+ const failure = error instanceof CheckFailure
206
+ ? error
207
+ : new CheckFailure('invalid_spec', `Flow "${path}" could not be checked as a Relayflow spec.`);
208
+ return { report: inputFailureReport(failure, path) };
209
+ }
210
+ }
211
+
117
212
  export function inputFailureReport(
118
213
  failure: { kind: CheckFailureKind; message: string },
119
214
  path?: string,
@@ -127,14 +222,15 @@ export function inputFailureReport(
127
222
  };
128
223
  }
129
224
 
130
- function readFlow(path: string): FlowSpec {
131
- let source: string;
225
+ function readFlowSource(path: string): string {
132
226
  try {
133
- source = readFileSync(path, 'utf8');
227
+ return readFileSync(path, 'utf8');
134
228
  } catch {
135
229
  throw new CheckFailure('input_unreadable', `Flow "${path}" is not readable.`);
136
230
  }
231
+ }
137
232
 
233
+ function readFlow(source: string, path: string): FlowSpec {
138
234
  let parsed: unknown;
139
235
  try {
140
236
  parsed = parseYaml(source);
@@ -161,7 +257,7 @@ function readFlow(path: string): FlowSpec {
161
257
  }
162
258
  }
163
259
 
164
- function readProjectConfig(start: string): ProjectConfig {
260
+ export function readProjectConfig(start: string): ProjectConfig {
165
261
  const configPath = findConfig(start);
166
262
  if (configPath === undefined) return { executors: [], models: [], directory: start };
167
263
  let value: unknown;
@@ -170,8 +266,16 @@ function readProjectConfig(start: string): ProjectConfig {
170
266
  } catch {
171
267
  throw new CheckFailure('config_invalid', `Project config "${configPath}" is not valid JSON.`);
172
268
  }
173
- if (!isObject(value) || Object.keys(value).some((key) => !['cli', 'executors', 'models'].includes(key))) {
174
- throw new CheckFailure('config_invalid', `Project config "${configPath}" expects only cli, executors, and models.`);
269
+ if (!isObject(value) || Object.keys(value).some((key) => !['cli', 'executors', 'models', 'mcp', 'deploy', 'plugins'].includes(key))) {
270
+ throw new CheckFailure('config_invalid', `Project config "${configPath}" expects only cli, executors, models, mcp, deploy, and plugins.`);
271
+ }
272
+ if (value['deploy'] !== undefined && (!isObject(value['deploy'])
273
+ || Object.keys(value['deploy']).some(key => key !== 'bucket')
274
+ || !isNonEmptyString(value['deploy']['bucket']))) {
275
+ throw new CheckFailure('config_invalid', `Project config "${configPath}" has invalid deploy.bucket.`);
276
+ }
277
+ if (value['plugins'] !== undefined && (!Array.isArray(value['plugins']) || !value['plugins'].every(isNonEmptyString))) {
278
+ throw new CheckFailure('config_invalid', 'Project plugins must be package names.');
175
279
  }
176
280
  if (value['cli'] !== undefined && !isNonEmptyString(value['cli'])) {
177
281
  throw new CheckFailure('config_invalid', `Project config "${configPath}" has an invalid cli.`);
@@ -193,7 +297,20 @@ function readProjectConfig(start: string): ProjectConfig {
193
297
  throw new CheckFailure('config_invalid', `Project config "${configPath}" has duplicate models.`);
194
298
  }
195
299
  }
300
+ let mcp: Record<string, McpServerConfig> | undefined;
301
+ if (value['mcp'] !== undefined) {
302
+ try { mcp = parseMcpConfig(value['mcp']); }
303
+ catch (error) {
304
+ throw new CheckFailure('config_invalid', `Project config "${configPath}": ${(error as Error).message}.`);
305
+ }
306
+ for (const entry of Object.values(mcp)) {
307
+ if ('command' in entry) entry.command = resolveExecutable(entry.command, dirname(configPath))
308
+ ?? canonicalCli(entry.command, dirname(configPath));
309
+ }
310
+ }
196
311
  return {
312
+ ...(value['deploy'] !== undefined ? { deploy: value['deploy'] as { bucket: string } } : {}),
313
+ ...(mcp !== undefined ? { mcp } : {}),
197
314
  ...(value['cli'] !== undefined ? { cli: value['cli'] as string } : {}),
198
315
  executors: (value['executors'] as string[] | undefined) ?? [],
199
316
  models: (value['models'] as string[] | undefined) ?? [],
@@ -220,6 +337,7 @@ function findConfig(start: string): string | undefined {
220
337
 
221
338
  function systemProbes(flowDirectory: string, config: ProjectConfig): PreflightProbes {
222
339
  return {
340
+ helper: helperReady,
223
341
  cli: (cli, source, model) => probeCli(cli, source === 'project' ? config.directory : flowDirectory, model),
224
342
  executor: (trigger) => config.executors.includes(trigger.executor),
225
343
  command: (binary) => executableExists(binary, flowDirectory),
@@ -0,0 +1,47 @@
1
+ import { CloudFlowError } from '../cloud-http.js';
2
+ import { runInCloud, waitForCloudFlowRun } from '../cloud-run.js';
3
+ import type { CliIo } from '../cli.js';
4
+
5
+ /** Presentation only: the central CLI parser owns argv; the SDK owns the lifecycle. */
6
+ export async function runCloudCli(
7
+ { value: path, json, wait }: { value: string; json: boolean; wait: boolean },
8
+ io: CliIo,
9
+ ): Promise<0 | 1 | 2> {
10
+ const controller = new AbortController();
11
+ const abort = (): void => controller.abort();
12
+ process.once('SIGINT', abort);
13
+ process.once('SIGTERM', abort);
14
+ let runId: string | undefined;
15
+ try {
16
+ const receipt = await runInCloud({ path }, { signal: controller.signal });
17
+ runId = receipt.runId;
18
+ if (!json) {
19
+ io.stdout(`ACCEPTED ${receipt.runId} (${receipt.status})`);
20
+ io.stdout(receipt.apiUrl);
21
+ }
22
+ if (!wait) {
23
+ if (json) io.stdout(JSON.stringify({ ok: true, ...receipt }));
24
+ return 0;
25
+ }
26
+ const run = await waitForCloudFlowRun(receipt.runId, { signal: controller.signal });
27
+ const ok = run.status === 'completed';
28
+ if (json) io.stdout(JSON.stringify({ ok, ...receipt, ...run }));
29
+ else io.stdout(`${run.status.toUpperCase()} ${run.runId} completionReason: ${'completionReason' in run ? run.completionReason : 'unavailable'}`);
30
+ return ok ? 0 : 1;
31
+ } catch (error) {
32
+ const code = controller.signal.aborted ? (runId ? 'observation_aborted' : 'admission_unknown')
33
+ : error instanceof CloudFlowError ? error.code : 'cloud_run_failed';
34
+ const message = controller.signal.aborted
35
+ ? runId ? 'Stopped observing; the hosted run has not been cancelled.'
36
+ : 'Submission interrupted before a receipt was received. Admission is unknown; Cloud may have started the run. Do not resubmit blindly.'
37
+ : error instanceof Error ? error.message : 'Cloud run failed.';
38
+ if (json) io.stdout(JSON.stringify({ ok: false, code, message, ...(runId ? { runId } : {}) }));
39
+ else io.stderr(`${code}: ${message}${runId ? ` (run ${runId})` : ''}`);
40
+ return error instanceof CloudFlowError
41
+ && (['configuration', 'unsupported_source', 'invalid_input'].includes(error.code)
42
+ || (runId === undefined && error.code === 'http_error' && [401, 403].includes(error.status ?? 0))) ? 2 : 1;
43
+ } finally {
44
+ process.off('SIGINT', abort);
45
+ process.off('SIGTERM', abort);
46
+ }
47
+ }