@relayflows/sdk 2.0.8 → 2.0.10

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 (474) 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-transport.d.ts +84 -0
  22. package/dist/agent-relay-transport.d.ts.map +1 -0
  23. package/dist/agent-relay-transport.js +134 -0
  24. package/dist/agent-relay-transport.js.map +1 -0
  25. package/dist/authored-budget.d.ts +13 -0
  26. package/dist/authored-budget.d.ts.map +1 -0
  27. package/dist/authored-budget.js +78 -0
  28. package/dist/authored-budget.js.map +1 -0
  29. package/dist/authored-flow-error.d.ts +1 -1
  30. package/dist/authored-flow-error.d.ts.map +1 -1
  31. package/dist/authored-flow-error.js.map +1 -1
  32. package/dist/authored-flow-executor.d.ts +13 -7
  33. package/dist/authored-flow-executor.d.ts.map +1 -1
  34. package/dist/authored-flow-executor.js +115 -194
  35. package/dist/authored-flow-executor.js.map +1 -1
  36. package/dist/authored-flow-loader.d.ts +10 -1
  37. package/dist/authored-flow-loader.d.ts.map +1 -1
  38. package/dist/authored-flow-loader.js +56 -3
  39. package/dist/authored-flow-loader.js.map +1 -1
  40. package/dist/authored-flow-operation.d.ts +9 -1
  41. package/dist/authored-flow-operation.d.ts.map +1 -1
  42. package/dist/authored-flow-operation.js +35 -4
  43. package/dist/authored-flow-operation.js.map +1 -1
  44. package/dist/authored-helper-effect.d.ts +12 -0
  45. package/dist/authored-helper-effect.d.ts.map +1 -0
  46. package/dist/authored-helper-effect.js +144 -0
  47. package/dist/authored-helper-effect.js.map +1 -0
  48. package/dist/authored-mcp.d.ts +17 -0
  49. package/dist/authored-mcp.d.ts.map +1 -0
  50. package/dist/authored-mcp.js +171 -0
  51. package/dist/authored-mcp.js.map +1 -0
  52. package/dist/authored-memory.d.ts +7 -0
  53. package/dist/authored-memory.d.ts.map +1 -0
  54. package/dist/authored-memory.js +60 -0
  55. package/dist/authored-memory.js.map +1 -0
  56. package/dist/authored-plugin-effect.d.ts +16 -0
  57. package/dist/authored-plugin-effect.d.ts.map +1 -0
  58. package/dist/authored-plugin-effect.js +163 -0
  59. package/dist/authored-plugin-effect.js.map +1 -0
  60. package/dist/authored-step-output.d.ts +23 -0
  61. package/dist/authored-step-output.d.ts.map +1 -0
  62. package/dist/authored-step-output.js +67 -0
  63. package/dist/authored-step-output.js.map +1 -0
  64. package/dist/authored-worker-step.d.ts +15 -0
  65. package/dist/authored-worker-step.d.ts.map +1 -0
  66. package/dist/authored-worker-step.js +136 -0
  67. package/dist/authored-worker-step.js.map +1 -0
  68. package/dist/budget-preflight.d.ts +5 -0
  69. package/dist/budget-preflight.d.ts.map +1 -0
  70. package/dist/budget-preflight.js +33 -0
  71. package/dist/budget-preflight.js.map +1 -0
  72. package/dist/budget.d.ts +14 -0
  73. package/dist/budget.d.ts.map +1 -0
  74. package/dist/budget.js +52 -0
  75. package/dist/budget.js.map +1 -0
  76. package/dist/bundle-transport.d.ts +18 -0
  77. package/dist/bundle-transport.d.ts.map +1 -0
  78. package/dist/bundle-transport.js +111 -0
  79. package/dist/bundle-transport.js.map +1 -0
  80. package/dist/bundle-typescript.d.ts +8 -0
  81. package/dist/bundle-typescript.d.ts.map +1 -0
  82. package/dist/bundle-typescript.js +108 -0
  83. package/dist/bundle-typescript.js.map +1 -0
  84. package/dist/bundle.d.ts +25 -0
  85. package/dist/bundle.d.ts.map +1 -0
  86. package/dist/bundle.js +199 -0
  87. package/dist/bundle.js.map +1 -0
  88. package/dist/child-stop.d.ts +59 -0
  89. package/dist/child-stop.d.ts.map +1 -0
  90. package/dist/child-stop.js +94 -0
  91. package/dist/child-stop.js.map +1 -0
  92. package/dist/cli/add.d.ts +6 -0
  93. package/dist/cli/add.d.ts.map +1 -0
  94. package/dist/cli/add.js +47 -0
  95. package/dist/cli/add.js.map +1 -0
  96. package/dist/cli/build.d.ts +13 -0
  97. package/dist/cli/build.d.ts.map +1 -0
  98. package/dist/cli/build.js +224 -0
  99. package/dist/cli/build.js.map +1 -0
  100. package/dist/cli/bundle-preflight.d.ts +5 -0
  101. package/dist/cli/bundle-preflight.d.ts.map +1 -0
  102. package/dist/cli/bundle-preflight.js +28 -0
  103. package/dist/cli/bundle-preflight.js.map +1 -0
  104. package/dist/cli/check-helper-body.d.ts +4 -0
  105. package/dist/cli/check-helper-body.d.ts.map +1 -0
  106. package/dist/cli/check-helper-body.js +15 -0
  107. package/dist/cli/check-helper-body.js.map +1 -0
  108. package/dist/cli/check-triggers.d.ts +14 -0
  109. package/dist/cli/check-triggers.d.ts.map +1 -0
  110. package/dist/cli/check-triggers.js +37 -0
  111. package/dist/cli/check-triggers.js.map +1 -0
  112. package/dist/cli/check-typescript.d.ts +20 -0
  113. package/dist/cli/check-typescript.d.ts.map +1 -0
  114. package/dist/cli/check-typescript.js +54 -0
  115. package/dist/cli/check-typescript.js.map +1 -0
  116. package/dist/cli/check.d.ts +36 -3
  117. package/dist/cli/check.d.ts.map +1 -1
  118. package/dist/cli/check.js +122 -11
  119. package/dist/cli/check.js.map +1 -1
  120. package/dist/cli/cloud-run.d.ts +8 -0
  121. package/dist/cli/cloud-run.d.ts.map +1 -0
  122. package/dist/cli/cloud-run.js +50 -0
  123. package/dist/cli/cloud-run.js.map +1 -0
  124. package/dist/cli/deploy.d.ts +9 -0
  125. package/dist/cli/deploy.d.ts.map +1 -0
  126. package/dist/cli/deploy.js +58 -0
  127. package/dist/cli/deploy.js.map +1 -0
  128. package/dist/cli/deterministic-failure.d.ts +5 -0
  129. package/dist/cli/deterministic-failure.d.ts.map +1 -0
  130. package/dist/cli/deterministic-failure.js +66 -0
  131. package/dist/cli/deterministic-failure.js.map +1 -0
  132. package/dist/cli/direct-run.d.ts.map +1 -1
  133. package/dist/cli/direct-run.js +83 -7
  134. package/dist/cli/direct-run.js.map +1 -1
  135. package/dist/cli/hn-monitor.d.ts.map +1 -1
  136. package/dist/cli/hn-monitor.js +2 -1
  137. package/dist/cli/hn-monitor.js.map +1 -1
  138. package/dist/cli/replay.d.ts +12 -0
  139. package/dist/cli/replay.d.ts.map +1 -0
  140. package/dist/cli/replay.js +82 -0
  141. package/dist/cli/replay.js.map +1 -0
  142. package/dist/cli/reuse.d.ts +8 -0
  143. package/dist/cli/reuse.d.ts.map +1 -0
  144. package/dist/cli/reuse.js +25 -0
  145. package/dist/cli/reuse.js.map +1 -0
  146. package/dist/cli/run-digest.d.ts +4 -0
  147. package/dist/cli/run-digest.d.ts.map +1 -0
  148. package/dist/cli/run-digest.js +24 -0
  149. package/dist/cli/run-digest.js.map +1 -0
  150. package/dist/cli/run.d.ts +16 -5
  151. package/dist/cli/run.d.ts.map +1 -1
  152. package/dist/cli/run.js +99 -17
  153. package/dist/cli/run.js.map +1 -1
  154. package/dist/cli/serve-webhook.d.ts +40 -0
  155. package/dist/cli/serve-webhook.d.ts.map +1 -0
  156. package/dist/cli/serve-webhook.js +233 -0
  157. package/dist/cli/serve-webhook.js.map +1 -0
  158. package/dist/cli/tick-runner.d.ts.map +1 -1
  159. package/dist/cli/tick-runner.js +2 -1
  160. package/dist/cli/tick-runner.js.map +1 -1
  161. package/dist/cli-adapter.d.ts +8 -14
  162. package/dist/cli-adapter.d.ts.map +1 -1
  163. package/dist/cli-adapter.js +19 -64
  164. package/dist/cli-adapter.js.map +1 -1
  165. package/dist/cli-watch.d.ts +13 -0
  166. package/dist/cli-watch.d.ts.map +1 -0
  167. package/dist/cli-watch.js +185 -0
  168. package/dist/cli-watch.js.map +1 -0
  169. package/dist/cli.d.ts +4 -0
  170. package/dist/cli.d.ts.map +1 -1
  171. package/dist/cli.js +387 -14
  172. package/dist/cli.js.map +1 -1
  173. package/dist/cloud-http.d.ts +22 -0
  174. package/dist/cloud-http.d.ts.map +1 -0
  175. package/dist/cloud-http.js +84 -0
  176. package/dist/cloud-http.js.map +1 -0
  177. package/dist/cloud-run.d.ts +38 -0
  178. package/dist/cloud-run.d.ts.map +1 -0
  179. package/dist/cloud-run.js +112 -0
  180. package/dist/cloud-run.js.map +1 -0
  181. package/dist/compile.d.ts +21 -3
  182. package/dist/compile.d.ts.map +1 -1
  183. package/dist/compile.js +97 -23
  184. package/dist/compile.js.map +1 -1
  185. package/dist/create-flow.d.ts +17 -0
  186. package/dist/create-flow.d.ts.map +1 -0
  187. package/dist/create-flow.js +72 -0
  188. package/dist/create-flow.js.map +1 -0
  189. package/dist/daemon-connection.d.ts +14 -0
  190. package/dist/daemon-connection.d.ts.map +1 -1
  191. package/dist/daemon-connection.js +33 -1
  192. package/dist/daemon-connection.js.map +1 -1
  193. package/dist/demo-hn-monitor.js +2 -1
  194. package/dist/demo-hn-monitor.js.map +1 -1
  195. package/dist/effect-channel.d.ts +30 -0
  196. package/dist/effect-channel.d.ts.map +1 -0
  197. package/dist/effect-channel.js +71 -0
  198. package/dist/effect-channel.js.map +1 -0
  199. package/dist/failure-kinds.d.ts +14 -3
  200. package/dist/failure-kinds.d.ts.map +1 -1
  201. package/dist/failure-kinds.js +27 -0
  202. package/dist/failure-kinds.js.map +1 -1
  203. package/dist/gate-contract.d.ts.map +1 -1
  204. package/dist/gate-contract.js +9 -0
  205. package/dist/gate-contract.js.map +1 -1
  206. package/dist/helper-preflight.d.ts +18 -0
  207. package/dist/helper-preflight.d.ts.map +1 -0
  208. package/dist/helper-preflight.js +32 -0
  209. package/dist/helper-preflight.js.map +1 -0
  210. package/dist/helper-storage.d.ts +5 -0
  211. package/dist/helper-storage.d.ts.map +1 -0
  212. package/dist/helper-storage.js +31 -0
  213. package/dist/helper-storage.js.map +1 -0
  214. package/dist/helper-writeback.d.ts +12 -0
  215. package/dist/helper-writeback.d.ts.map +1 -0
  216. package/dist/helper-writeback.js +95 -0
  217. package/dist/helper-writeback.js.map +1 -0
  218. package/dist/index.d.ts +10 -2
  219. package/dist/index.d.ts.map +1 -1
  220. package/dist/index.js +7 -1
  221. package/dist/index.js.map +1 -1
  222. package/dist/input-binding.d.ts +4 -0
  223. package/dist/input-binding.d.ts.map +1 -0
  224. package/dist/input-binding.js +78 -0
  225. package/dist/input-binding.js.map +1 -0
  226. package/dist/journal-client.d.ts +12 -4
  227. package/dist/journal-client.d.ts.map +1 -1
  228. package/dist/journal-client.js +17 -6
  229. package/dist/journal-client.js.map +1 -1
  230. package/dist/journal-offset.d.ts +3 -0
  231. package/dist/journal-offset.d.ts.map +1 -0
  232. package/dist/journal-offset.js +69 -0
  233. package/dist/journal-offset.js.map +1 -0
  234. package/dist/journal-reader.d.ts +30 -0
  235. package/dist/journal-reader.d.ts.map +1 -0
  236. package/dist/journal-reader.js +156 -0
  237. package/dist/journal-reader.js.map +1 -0
  238. package/dist/json-schema.d.ts +2 -0
  239. package/dist/json-schema.d.ts.map +1 -1
  240. package/dist/json-schema.js +38 -22
  241. package/dist/json-schema.js.map +1 -1
  242. package/dist/llm-worker.d.ts +16 -0
  243. package/dist/llm-worker.d.ts.map +1 -0
  244. package/dist/llm-worker.js +79 -0
  245. package/dist/llm-worker.js.map +1 -0
  246. package/dist/local-agent.d.ts +8 -0
  247. package/dist/local-agent.d.ts.map +1 -0
  248. package/dist/local-agent.js +25 -0
  249. package/dist/local-agent.js.map +1 -0
  250. package/dist/mcp-client.d.ts +15 -0
  251. package/dist/mcp-client.d.ts.map +1 -0
  252. package/dist/mcp-client.js +104 -0
  253. package/dist/mcp-client.js.map +1 -0
  254. package/dist/mcp-config.d.ts +4 -0
  255. package/dist/mcp-config.d.ts.map +1 -0
  256. package/dist/mcp-config.js +61 -0
  257. package/dist/mcp-config.js.map +1 -0
  258. package/dist/mcp-stdio.d.ts +24 -0
  259. package/dist/mcp-stdio.d.ts.map +1 -0
  260. package/dist/mcp-stdio.js +89 -0
  261. package/dist/mcp-stdio.js.map +1 -0
  262. package/dist/model-pricing.d.ts +22 -0
  263. package/dist/model-pricing.d.ts.map +1 -0
  264. package/dist/model-pricing.js +30 -0
  265. package/dist/model-pricing.js.map +1 -0
  266. package/dist/mount-registry.d.ts +9 -0
  267. package/dist/mount-registry.d.ts.map +1 -0
  268. package/dist/mount-registry.js +47 -0
  269. package/dist/mount-registry.js.map +1 -0
  270. package/dist/named-gate-lowering.d.ts +9 -0
  271. package/dist/named-gate-lowering.d.ts.map +1 -0
  272. package/dist/named-gate-lowering.js +152 -0
  273. package/dist/named-gate-lowering.js.map +1 -0
  274. package/dist/named-gates.d.ts +10 -0
  275. package/dist/named-gates.d.ts.map +1 -0
  276. package/dist/named-gates.js +69 -0
  277. package/dist/named-gates.js.map +1 -0
  278. package/dist/observer-link.d.ts +129 -0
  279. package/dist/observer-link.d.ts.map +1 -0
  280. package/dist/observer-link.js +274 -0
  281. package/dist/observer-link.js.map +1 -0
  282. package/dist/plugin-loader.d.ts +13 -0
  283. package/dist/plugin-loader.d.ts.map +1 -0
  284. package/dist/plugin-loader.js +110 -0
  285. package/dist/plugin-loader.js.map +1 -0
  286. package/dist/plugin-manifest.d.ts +27 -0
  287. package/dist/plugin-manifest.d.ts.map +1 -0
  288. package/dist/plugin-manifest.js +89 -0
  289. package/dist/plugin-manifest.js.map +1 -0
  290. package/dist/preflight.d.ts +26 -2
  291. package/dist/preflight.d.ts.map +1 -1
  292. package/dist/preflight.js +175 -1
  293. package/dist/preflight.js.map +1 -1
  294. package/dist/progress.d.ts +14 -0
  295. package/dist/progress.d.ts.map +1 -0
  296. package/dist/progress.js +39 -0
  297. package/dist/progress.js.map +1 -0
  298. package/dist/protocol.d.ts +11 -0
  299. package/dist/protocol.d.ts.map +1 -1
  300. package/dist/pty-sidechannel.d.ts +14 -0
  301. package/dist/pty-sidechannel.d.ts.map +1 -0
  302. package/dist/pty-sidechannel.js +103 -0
  303. package/dist/pty-sidechannel.js.map +1 -0
  304. package/dist/scope-compiler.d.ts +52 -0
  305. package/dist/scope-compiler.d.ts.map +1 -0
  306. package/dist/scope-compiler.js +62 -0
  307. package/dist/scope-compiler.js.map +1 -0
  308. package/dist/slack-preflight.d.ts +6 -0
  309. package/dist/slack-preflight.d.ts.map +1 -0
  310. package/dist/slack-preflight.js +28 -0
  311. package/dist/slack-preflight.js.map +1 -0
  312. package/dist/slack-writeback.d.ts +40 -0
  313. package/dist/slack-writeback.d.ts.map +1 -0
  314. package/dist/slack-writeback.js +60 -0
  315. package/dist/slack-writeback.js.map +1 -0
  316. package/dist/spec.d.ts +134 -4
  317. package/dist/spec.d.ts.map +1 -1
  318. package/dist/spec.js.map +1 -1
  319. package/dist/step-dependencies.d.ts.map +1 -1
  320. package/dist/step-dependencies.js +2 -1
  321. package/dist/step-dependencies.js.map +1 -1
  322. package/dist/step-fields.d.ts +4 -4
  323. package/dist/step-fields.d.ts.map +1 -1
  324. package/dist/step-fields.js +5 -2
  325. package/dist/step-fields.js.map +1 -1
  326. package/dist/trigger-executor.d.ts +7 -0
  327. package/dist/trigger-executor.d.ts.map +1 -0
  328. package/dist/trigger-executor.js +35 -0
  329. package/dist/trigger-executor.js.map +1 -0
  330. package/dist/validate.d.ts.map +1 -1
  331. package/dist/validate.js +53 -6
  332. package/dist/validate.js.map +1 -1
  333. package/dist/webhook-rate-limit.d.ts +29 -0
  334. package/dist/webhook-rate-limit.d.ts.map +1 -0
  335. package/dist/webhook-rate-limit.js +38 -0
  336. package/dist/webhook-rate-limit.js.map +1 -0
  337. package/dist/webhook-signature.d.ts +22 -0
  338. package/dist/webhook-signature.d.ts.map +1 -0
  339. package/dist/webhook-signature.js +53 -0
  340. package/dist/webhook-signature.js.map +1 -0
  341. package/dist/worker-cli.d.ts +13 -1
  342. package/dist/worker-cli.d.ts.map +1 -1
  343. package/dist/worker-cli.js +133 -12
  344. package/dist/worker-cli.js.map +1 -1
  345. package/dist/worker-input.d.ts +4 -0
  346. package/dist/worker-input.d.ts.map +1 -0
  347. package/dist/worker-input.js +14 -0
  348. package/dist/worker-input.js.map +1 -0
  349. package/dist/worker-lease.d.ts +5 -0
  350. package/dist/worker-lease.d.ts.map +1 -0
  351. package/dist/worker-lease.js +80 -0
  352. package/dist/worker-lease.js.map +1 -0
  353. package/dist/worker-spend.d.ts +19 -0
  354. package/dist/worker-spend.d.ts.map +1 -0
  355. package/dist/worker-spend.js +22 -0
  356. package/dist/worker-spend.js.map +1 -0
  357. package/dist/worker-usage.d.ts +7 -0
  358. package/dist/worker-usage.d.ts.map +1 -0
  359. package/dist/worker-usage.js +54 -0
  360. package/dist/worker-usage.js.map +1 -0
  361. package/dist/worker.d.ts +2 -0
  362. package/dist/worker.d.ts.map +1 -1
  363. package/dist/worker.js +22 -8
  364. package/dist/worker.js.map +1 -1
  365. package/dist/wrapper-session.d.ts +1 -1
  366. package/dist/wrapper-session.d.ts.map +1 -1
  367. package/dist/wrapper-session.js +56 -19
  368. package/dist/wrapper-session.js.map +1 -1
  369. package/dist/yaml-helper-effect.d.ts +8 -0
  370. package/dist/yaml-helper-effect.d.ts.map +1 -0
  371. package/dist/yaml-helper-effect.js +79 -0
  372. package/dist/yaml-helper-effect.js.map +1 -0
  373. package/dist/yaml-helpers.d.ts +18 -0
  374. package/dist/yaml-helpers.d.ts.map +1 -0
  375. package/dist/yaml-helpers.js +116 -0
  376. package/dist/yaml-helpers.js.map +1 -0
  377. package/package.json +19 -2
  378. package/src/adapters/base.ts +53 -0
  379. package/src/adapters/claude.ts +55 -0
  380. package/src/adapters/codex.ts +56 -0
  381. package/src/adapters/index.ts +36 -0
  382. package/src/adapters/wrapper.ts +42 -0
  383. package/src/agent-relay-transport.ts +187 -0
  384. package/src/authored-budget.ts +69 -0
  385. package/src/authored-flow-error.ts +10 -0
  386. package/src/authored-flow-executor.ts +170 -293
  387. package/src/authored-flow-loader.ts +60 -3
  388. package/src/authored-flow-operation.ts +38 -5
  389. package/src/authored-helper-effect.ts +143 -0
  390. package/src/authored-mcp.ts +166 -0
  391. package/src/authored-memory.ts +55 -0
  392. package/src/authored-plugin-effect.ts +152 -0
  393. package/src/authored-step-output.ts +107 -0
  394. package/src/authored-worker-step.ts +181 -0
  395. package/src/budget-preflight.ts +31 -0
  396. package/src/budget.ts +49 -0
  397. package/src/bundle-transport.ts +79 -0
  398. package/src/bundle-typescript.ts +98 -0
  399. package/src/bundle.ts +182 -0
  400. package/src/child-stop.ts +138 -0
  401. package/src/cli/add.ts +45 -0
  402. package/src/cli/build.ts +214 -0
  403. package/src/cli/bundle-preflight.ts +28 -0
  404. package/src/cli/check-helper-body.ts +14 -0
  405. package/src/cli/check-triggers.ts +41 -0
  406. package/src/cli/check-typescript.ts +63 -0
  407. package/src/cli/check.ts +132 -14
  408. package/src/cli/cloud-run.ts +47 -0
  409. package/src/cli/deploy.ts +52 -0
  410. package/src/cli/deterministic-failure.ts +69 -0
  411. package/src/cli/direct-run.ts +79 -7
  412. package/src/cli/hn-monitor.ts +2 -1
  413. package/src/cli/replay.ts +81 -0
  414. package/src/cli/reuse.ts +23 -0
  415. package/src/cli/run-digest.ts +24 -0
  416. package/src/cli/run.ts +104 -21
  417. package/src/cli/serve-webhook.ts +246 -0
  418. package/src/cli/tick-runner.ts +2 -1
  419. package/src/cli-adapter.ts +31 -74
  420. package/src/cli-watch.ts +174 -0
  421. package/src/cli.ts +396 -17
  422. package/src/cloud-http.ts +100 -0
  423. package/src/cloud-run.ts +137 -0
  424. package/src/compile.ts +102 -23
  425. package/src/create-flow.ts +82 -0
  426. package/src/daemon-connection.ts +32 -1
  427. package/src/demo-hn-monitor.ts +2 -1
  428. package/src/effect-channel.ts +97 -0
  429. package/src/failure-kinds.ts +38 -0
  430. package/src/gate-contract.ts +7 -0
  431. package/src/helper-preflight.ts +34 -0
  432. package/src/helper-storage.ts +23 -0
  433. package/src/helper-writeback.ts +83 -0
  434. package/src/index.ts +26 -0
  435. package/src/input-binding.ts +76 -0
  436. package/src/journal-client.ts +20 -7
  437. package/src/journal-offset.ts +60 -0
  438. package/src/journal-reader.ts +165 -0
  439. package/src/json-schema.ts +34 -18
  440. package/src/llm-worker.ts +82 -0
  441. package/src/local-agent.ts +30 -0
  442. package/src/mcp-client.ts +97 -0
  443. package/src/mcp-config.ts +42 -0
  444. package/src/mcp-stdio.ts +84 -0
  445. package/src/model-pricing.ts +32 -0
  446. package/src/mount-registry.ts +44 -0
  447. package/src/named-gate-lowering.ts +147 -0
  448. package/src/named-gates.ts +76 -0
  449. package/src/observer-link.ts +368 -0
  450. package/src/plugin-loader.ts +88 -0
  451. package/src/plugin-manifest.ts +76 -0
  452. package/src/preflight.ts +178 -4
  453. package/src/progress.ts +51 -0
  454. package/src/protocol.ts +12 -0
  455. package/src/pty-sidechannel.ts +92 -0
  456. package/src/scope-compiler.ts +105 -0
  457. package/src/slack-preflight.ts +25 -0
  458. package/src/slack-writeback.ts +63 -0
  459. package/src/spec.ts +112 -4
  460. package/src/step-dependencies.ts +3 -1
  461. package/src/step-fields.ts +5 -2
  462. package/src/trigger-executor.ts +34 -0
  463. package/src/validate.ts +47 -6
  464. package/src/webhook-rate-limit.ts +69 -0
  465. package/src/webhook-signature.ts +74 -0
  466. package/src/worker-cli.ts +156 -11
  467. package/src/worker-input.ts +15 -0
  468. package/src/worker-lease.ts +85 -0
  469. package/src/worker-spend.ts +23 -0
  470. package/src/worker-usage.ts +48 -0
  471. package/src/worker.ts +27 -9
  472. package/src/wrapper-session.ts +50 -15
  473. package/src/yaml-helper-effect.ts +78 -0
  474. package/src/yaml-helpers.ts +131 -0
package/src/cli.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { addPlugin } from './cli/add.js';
3
+ import { watchCheck } from './cli-watch.js';
4
+ import { checkHelperBody } from './cli/check-helper-body.js';
2
5
 
6
+ import { renderProgress, type ProgressEvent } from './progress.js';
3
7
  import { realpathSync } from 'node:fs';
4
8
  import { pathToFileURL } from 'node:url';
5
9
  import {
@@ -14,10 +18,23 @@ import {
14
18
  type RunProgress,
15
19
  type RunReport,
16
20
  } from './cli/run.js';
21
+ import { checkAuthoredTriggers } from './cli/check-triggers.js';
22
+ import { parseWebhookArgs, runServeWebhook } from './cli/serve-webhook.js';
17
23
  import { runDirectFlow } from './cli/direct-run.js';
24
+ import { parseReplayArgs, replayJournal, type ReplayArgs } from './cli/replay.js';
25
+ import { checkTypeScriptFlow } from './cli/check-typescript.js';
26
+ import { runCloudCli } from './cli/cloud-run.js';
18
27
  import { isAuthoredFlowPath } from './direct-input.js';
28
+ import { parseDeployArgs, runDeploy, type DeployArgs } from './cli/deploy.js';
29
+ import { parseDigestReference } from './bundle-transport.js';
30
+ import { parseBuildArgs, runBuild, type BuildArgs } from './cli/build.js';
19
31
  import { runHnMonitor } from './cli/hn-monitor.js';
20
32
  import { runTickRunner } from './cli/tick-runner.js';
33
+ import {
34
+ mintObserverUrl,
35
+ resolveObserverLinkEnv,
36
+ type MintObserverOptions,
37
+ } from './observer-link.js';
21
38
 
22
39
  export type { CheckInputDiagnostic, CheckReport } from './cli/check.js';
23
40
 
@@ -28,9 +45,16 @@ export interface CliIo {
28
45
 
29
46
  type CliExitCode = 0 | 1 | 2 | 3;
30
47
  type ParsedArgs =
31
- | { command: 'check'; json: boolean; value: string }
32
- | { command: 'run'; dataDir: string; input: string | undefined; json: boolean; spawn: boolean; value: string }
33
- | { command: 'resume'; dataDir: string; json: boolean; spawn: boolean; value: string }
48
+ | { command: 'add'; value: string }
49
+ | ReplayArgs
50
+ | BuildArgs
51
+ | DeployArgs
52
+ | { command: 'serve-webhook'; dataDir: string; port: number; admitted?: readonly string[] }
53
+ | { command: 'cloud-run'; value: string; json: boolean; wait: boolean }
54
+ | { command: 'check'; json: boolean; watch: boolean; value: string }
55
+ | { command: 'run'; bucket: string | undefined; reuseFromRunId: string | undefined; localAgent: boolean; dataDir: string; input: string | undefined; json: boolean; spawn: boolean; noObserverLink: boolean; allowHumanInfluenced: boolean; value: string }
56
+ | { command: 'resume'; dataDir: string; json: boolean; spawn: boolean; noObserverLink: boolean; allowHumanInfluenced: boolean; value: string }
57
+ | { command: 'observer'; dataDir: string }
34
58
  | { command: 'hn-monitor'; sub: 'start'; dataDir: string; specPath: string; pollIntervalMs: number | undefined }
35
59
  | { command: 'tick'; sub: 'start'; dataDir: string; specPath: string; scheduleId: string;
36
60
  intervalMs: number; epochMs: number | undefined; maxCatchUp: number | undefined;
@@ -39,13 +63,22 @@ type ParsedArgs =
39
63
  const DEFAULT_DATA_DIR = '.relayflowd';
40
64
  const USAGE = [
41
65
  'Usage:',
42
- 'flows check [--json] <flow.yaml|spec.json>',
43
- 'flows run [--json] [--no-spawn] [--data-dir <dir>] <flow.yaml|spec.json>',
44
- 'flows run [--json] [--no-spawn] [--data-dir <dir>] <flow.ts> --input <inline-json-or-file>',
66
+ 'flows add <helper-name|@flows/helper-name>',
67
+ 'flows build [--out <dir>] <flow.yaml|flow.ts>',
68
+ 'flows build --verify <bundle-dir>',
69
+ 'flows deploy <flow>@sha256:<digest> --to <file-bucket-uri>',
70
+ 'flows run <flow>@sha256:<digest> [--bucket <file-bucket-uri>] [--data-dir <dir>] [--json]',
71
+ 'flows check [--watch] [--json] <flow.ts|flow.yaml|spec.json>',
72
+ 'flows serve-webhook --data-dir <dir> --port <p> [--allow <name>[,<name>]]',
73
+ 'flows run [--json] [--no-spawn] [--no-observer-link] [--data-dir <dir>] [--local-agent] [--reuse-from <run-id>] <flow.yaml|spec.json>',
74
+ 'flows run --cloud [--json] [--wait] <flow.yaml|spec.json>',
75
+ 'flows run [--json] [--no-spawn] [--no-observer-link] [--data-dir <dir>] [--local-agent] <flow.ts> --input <inline-json-or-file>',
45
76
  '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>',
46
- 'flows resume [--json] [--no-spawn] [--data-dir <dir>] <run-id>',
77
+ 'flows resume [--allow-human-influenced] [--json] [--no-spawn] [--no-observer-link] [--data-dir <dir>] <run-id>',
78
+ 'flows replay [--allow-human-influenced] [--json] [--data-dir <dir>] <run-id> [--at <step-id>]',
79
+ 'flows observer [--data-dir <dir>]',
47
80
  'flows hn-monitor start [--data-dir <dir>] [--poll-interval-ms <n>] <spec.json>',
48
- ].join(' ');
81
+ ].join('\n');
49
82
 
50
83
  /**
51
84
  * `FLOWS_NO_SPAWN=1` is `--no-spawn` for a whole environment: the lever for CI
@@ -66,6 +99,11 @@ export async function runCli(
66
99
  args: readonly string[],
67
100
  io: CliIo = PROCESS_IO,
68
101
  ): Promise<CliExitCode> {
102
+ if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
103
+ io.stdout(USAGE);
104
+ return 0;
105
+ }
106
+
69
107
  const parsed = parseArgs(args);
70
108
  if (parsed === undefined) {
71
109
  const report = inputFailureReport({ kind: 'invalid_invocation', message: USAGE });
@@ -73,17 +111,30 @@ export async function runCli(
73
111
  return 2;
74
112
  }
75
113
 
114
+ if (parsed.command === 'add') return addPlugin(parsed.value, io);
115
+
116
+ if (parsed.command === 'serve-webhook') return runServeWebhook(parsed, io);
117
+
118
+ if (parsed.command === 'cloud-run') return runCloudCli(parsed, io);
119
+ if (parsed.command === 'replay') return replayJournal(parsed, io);
120
+ if (parsed.command === 'build') return runBuild(parsed, io);
121
+ if (parsed.command === 'deploy') return runDeploy(parsed, io);
122
+
76
123
  if (parsed.command === 'check') {
124
+ if (parsed.watch) return watchCheck(parsed.value, parsed.json, io);
77
125
  // Deliberately daemon-free (kernel/DAEMON-LIFECYCLE.md §4). `checkFlow` is
78
- // a pure compile-and-preflight that opens no socket, and the parser
126
+ // a compile-and-preflight that opens no daemon socket, and the parser
79
127
  // refuses `--data-dir` on `check`, so there is no data dir to attach to.
80
128
  // `flows check` keeps working with no daemon, no relayflowd binary and no
81
129
  // data directory at all -- a property worth keeping, not an omission.
82
- const checked = checkFlow(parsed.value);
130
+ const checked = /\.(?:[cm]?[jt]s)$/.test(parsed.value)
131
+ ? await checkAuthoredFlowComposed(parsed.value) : checkFlow(parsed.value);
83
132
  emitCheckReport(checked.report, parsed.json, io);
84
133
  return checked.report.ok ? 0 : 2;
85
134
  }
86
135
 
136
+ if (parsed.command === 'observer') return runObserverCommand(io);
137
+
87
138
  if (parsed.command === 'hn-monitor') {
88
139
  const controller = new AbortController();
89
140
  const onSignal = (): void => controller.abort();
@@ -130,19 +181,229 @@ export async function runCli(
130
181
  // single `connect()` seam immediately before journal-client.ts is used --
131
182
  // not here. Hoisting it above the dispatch would start a daemon as a side
132
183
  // effect of an invocation that is about to be refused for bad input.
184
+ const startedSteps = new Map<string, number>();
185
+ const showProgress = (event: ProgressEvent): void => {
186
+ if (event.type === 'step.started') startedSteps.set(event.stepId, performance.now());
187
+ if (!parsed.json) for (const line of renderProgress([event])) io.stderr(line);
188
+ };
133
189
  const lifecycle = {
134
- onWait: (progress: RunProgress) => emitWait(progress, io),
190
+ ...(parsed.command === 'run' ? { bucket: parsed.bucket } : {}),
191
+ allowHumanInfluenced: parsed.allowHumanInfluenced,
192
+ onPtyReady: (path: string) => io.stderr(`PTY ${path}`),
193
+ ...(parsed.command === 'run' && parsed.reuseFromRunId !== undefined ? { reuseFromRunId: parsed.reuseFromRunId } : {}),
194
+ localAgent: parsed.command === 'run' && parsed.localAgent,
195
+ onProgress: showProgress,
196
+ onWait: (progress: RunProgress) => {
197
+ emitWait(progress, io);
198
+ const now = performance.now();
199
+ if (!startedSteps.has(progress.stepId)) startedSteps.set(progress.stepId, now);
200
+ showProgress({ type: 'step.running', stepId: progress.stepId, stepType: progress.stepType,
201
+ elapsedMs: now - startedSteps.get(progress.stepId)! });
202
+ },
135
203
  daemon: { spawn: parsed.spawn && spawnAllowedByEnv() },
136
204
  };
205
+ // Mint the observer token in parallel with the run so the mint round-trip
206
+ // never adds to the RUN summary latency. The outcome is only consulted at
207
+ // emit time; a rejected promise here can never fail the run (see
208
+ // `observerUrlFrom`, which swallows every failure into `warning`).
209
+ const observerMint = startObserverMint(parsed);
137
210
  const execution = parsed.command === 'run'
138
211
  ? isAuthoredFlowPath(parsed.value)
139
212
  ? await runDirectFlow(parsed.value, parsed.input, parsed.dataDir, lifecycle)
140
213
  : await runFlow(parsed.value, parsed.dataDir, lifecycle)
141
214
  : await resumeFlow(parsed.value, parsed.dataDir, lifecycle);
215
+ // In `--json` mode the report is a single machine-readable object that
216
+ // MUST carry `observerUrl` when one is available, so a consumer sees one
217
+ // authoritative signal. That justifies blocking up to `MINT_TIMEOUT_MS`
218
+ // on the mint before emit -- consumers can wait for a bounded time.
219
+ //
220
+ // In plain-text mode the `RUN` line and any check diagnostics carry the
221
+ // primary signal; the observer URL is a nice-to-have follow-up. Blocking
222
+ // the RUN summary on a stalled Relaycast call (up to 5s per failed
223
+ // preflight) is worse than printing `Observer:` on a later line, so we
224
+ // emit the run report immediately and finalize the observer link after.
225
+ if (parsed.json) {
226
+ const observerUrl = await observerUrlFrom(observerMint, io);
227
+ emitRunReport(execution, parsed.json, io, observerUrl);
228
+ return execution.exitCode;
229
+ }
142
230
  emitRunReport(execution, parsed.json, io);
231
+ await finalizeObserverLine(observerMint, io);
143
232
  return execution.exitCode;
144
233
  }
145
234
 
235
+ /**
236
+ * Compose helper-body checks (from f.slack aspect) with TS-flow MCP checks
237
+ * so an authored .flow.ts surfaces diagnostics from both. Both check
238
+ * functions load the authored flow independently — this can be flattened
239
+ * later, but composing them keeps the rebase minimal and preserves both
240
+ * aspects' coverage.
241
+ */
242
+ async function checkAuthoredFlowComposed(path: string): Promise<{ report: CheckReport }> {
243
+ const helper = await checkHelperBody(path);
244
+ if (!helper.report.ok) return helper;
245
+ const mcp = await checkTypeScriptFlow(path);
246
+ const triggers = isAuthoredFlowPath(path)
247
+ ? await checkAuthoredTriggers(path)
248
+ : undefined;
249
+ const triggerDiagnostics = triggers?.report.diagnostics ?? [];
250
+ const triggerOk = triggers?.report.ok ?? true;
251
+ return {
252
+ report: {
253
+ ...mcp.report,
254
+ diagnostics: [...helper.report.diagnostics, ...mcp.report.diagnostics, ...triggerDiagnostics],
255
+ ok: helper.report.ok && mcp.report.ok && triggerOk,
256
+ },
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Start the observer-token mint if the environment says one should happen.
262
+ * Returns `undefined` when no attempt should be made — no workspace key
263
+ * configured, or `FLOWS_NO_OBSERVER=1` / `--no-observer-link` — which is the
264
+ * silent-skip branch. The returned promise always resolves; a rejection here
265
+ * would slip past `observerUrlFrom` and could fail the run, which the feature
266
+ * expressly forbids.
267
+ */
268
+ function startObserverMint(
269
+ parsed: { command: 'run' | 'resume'; noObserverLink: boolean },
270
+ env: NodeJS.ProcessEnv = process.env,
271
+ mint: (options: MintObserverOptions) => Promise<{ observerUrl?: string; warning?: string }> = mintObserverUrl,
272
+ ): Promise<{ observerUrl?: string; warning?: string }> | undefined {
273
+ if (parsed.noObserverLink) return undefined;
274
+ // `resolveObserverLinkEnv` (not `readObserverLinkEnv`) falls back to the
275
+ // `agent-relay cloud login` workspace store (~/.agentworkforce/relay/
276
+ // workspaces.json) when RELAYCAST_WORKSPACE_KEY is unset. Env wins if set;
277
+ // FLOWS_NO_OBSERVER=1 still suppresses regardless of source.
278
+ const link = resolveObserverLinkEnv(env);
279
+ if (link.suppressed || link.workspaceKey === undefined) return undefined;
280
+ return mint({
281
+ workspaceKey: link.workspaceKey,
282
+ ...(link.baseUrl !== undefined ? { baseUrl: link.baseUrl } : {}),
283
+ ...(link.dashboardUrl !== undefined ? { dashboardUrl: link.dashboardUrl } : {}),
284
+ }).catch((error) => ({
285
+ warning: error instanceof Error ? error.message : 'unknown mint error',
286
+ }));
287
+ }
288
+
289
+ /**
290
+ * Resolve the pending observer-mint into a URL (or nothing), and route any
291
+ * mint warning to stderr under a `[observer]` label. Silent-skip (`undefined`
292
+ * input) prints nothing at all, so a workspace with no observer configured
293
+ * produces no observer output on stderr or stdout.
294
+ */
295
+ async function observerUrlFrom(
296
+ mint: Promise<{ observerUrl?: string; warning?: string }> | undefined,
297
+ io: CliIo,
298
+ ): Promise<string | undefined> {
299
+ if (mint === undefined) return undefined;
300
+ const outcome = await mint;
301
+ if (outcome.warning !== undefined) {
302
+ io.stderr(`[observer] token mint failed: ${outcome.warning}; skipping observer link`);
303
+ }
304
+ return outcome.observerUrl;
305
+ }
306
+
307
+ /**
308
+ * Grace budget the plain-text emit path waits for a still-pending mint after
309
+ * the RUN summary is out. `mintObserverUrl` already caps its own network
310
+ * round-trip at `MINT_TIMEOUT_MS` (5s). The grace matches that ceiling so a
311
+ * slow-but-legitimate mint (empirically ~1.6s cold against
312
+ * `cast.agentrelay.com`) is not clipped by a shorter grace. A mint that has
313
+ * not resolved by 5s is genuinely stuck.
314
+ */
315
+ const OBSERVER_FINALIZE_GRACE_MS = 5_000;
316
+
317
+ /**
318
+ * Finalize the plain-text observer line after the RUN summary is already on
319
+ * stdout. If the mint resolves in time, print `Observer: <url>` on its own
320
+ * stdout line (matching v1 relayflows' convention). If it fails, print the
321
+ * standard `[observer]` diagnostic on stderr. If it is still pending after
322
+ * `OBSERVER_FINALIZE_GRACE_MS`, print a distinct stderr line so the operator
323
+ * knows the mint did not complete rather than seeing silence -- and stop
324
+ * waiting so the CLI can exit.
325
+ */
326
+ /**
327
+ * `flows observer`: mint and print a single observer URL. Reuses the same
328
+ * env parsing (`readObserverLinkEnv`) and mint (`mintObserverUrl`) as the
329
+ * run/resume verbs, so an operator who already has an observer URL working
330
+ * via `flows run` will get the identical URL shape here. Every failure
331
+ * path -- unset key, suppressed via `FLOWS_NO_OBSERVER`, mint HTTP error,
332
+ * network error, malformed response -- is refused with `REFUSED
333
+ * [observer_link_unavailable] <reason>` on stderr and exit 2, matching the
334
+ * flows CLI refusal shape (see `parseArgs` -> `invalid_invocation`).
335
+ */
336
+ async function runObserverCommand(
337
+ io: CliIo,
338
+ env: NodeJS.ProcessEnv = process.env,
339
+ mint: (options: MintObserverOptions) => Promise<{ observerUrl?: string; warning?: string }> = mintObserverUrl,
340
+ ): Promise<CliExitCode> {
341
+ // Same resolution as `flows run`: env wins, then the `agent-relay cloud
342
+ // login` workspace store. The refusal message names the env var because
343
+ // that is the primary path an operator is expected to configure; the
344
+ // cloud-login fallback is convenience, not the documented contract.
345
+ const link = resolveObserverLinkEnv(env);
346
+ if (link.suppressed) {
347
+ io.stderr('REFUSED [observer_link_unavailable] mint suppressed by FLOWS_NO_OBSERVER=1');
348
+ return 2;
349
+ }
350
+ if (link.workspaceKey === undefined) {
351
+ io.stderr(
352
+ 'REFUSED [observer_link_unavailable] no workspace key configured; '
353
+ + 'set RELAYCAST_WORKSPACE_KEY or run `agent-relay workspace set_key`',
354
+ );
355
+ return 2;
356
+ }
357
+ const outcome: { observerUrl?: string; warning?: string } = await mint({
358
+ workspaceKey: link.workspaceKey,
359
+ ...(link.baseUrl !== undefined ? { baseUrl: link.baseUrl } : {}),
360
+ }).catch((error): { warning: string } => ({
361
+ warning: error instanceof Error ? error.message : 'unknown mint error',
362
+ }));
363
+ if (outcome.observerUrl === undefined) {
364
+ // Fold the mint's own warning into the refusal so an operator sees the
365
+ // same phrasing they would have gotten under `flows run` -- no extra
366
+ // interpretation, no lossy summary.
367
+ const reason = outcome.warning ?? 'mint returned no URL';
368
+ io.stderr(`REFUSED [observer_link_unavailable] ${reason}`);
369
+ return 2;
370
+ }
371
+ io.stdout(outcome.observerUrl);
372
+ return 0;
373
+ }
374
+
375
+ export async function finalizeObserverLine(
376
+ mint: Promise<{ observerUrl?: string; warning?: string }> | undefined,
377
+ io: CliIo,
378
+ graceMs: number = OBSERVER_FINALIZE_GRACE_MS,
379
+ ): Promise<void> {
380
+ if (mint === undefined) return;
381
+ // A race between the mint promise and a bounded timer. Using a sentinel
382
+ // symbol (not `undefined`) so we can tell "grace expired" from "mint
383
+ // resolved to no URL and no warning" -- which should never happen, but
384
+ // must not silently claim a timeout if it does.
385
+ const TIMED_OUT: unique symbol = Symbol('observer-finalize-timeout') as never;
386
+ let timer: NodeJS.Timeout | undefined;
387
+ const timeout = new Promise<typeof TIMED_OUT>((resolve) => {
388
+ timer = setTimeout(() => resolve(TIMED_OUT), graceMs);
389
+ // Do not let this timer prolong the event loop past a clean exit; if the
390
+ // mint promise resolves first we clear it below, if the process is
391
+ // otherwise idle Node should not wait for us to give up.
392
+ timer.unref?.();
393
+ });
394
+ const outcome = await Promise.race([mint, timeout]);
395
+ if (timer !== undefined) clearTimeout(timer);
396
+ if (outcome === TIMED_OUT) {
397
+ io.stderr('[observer] mint did not complete in time; skipping observer link');
398
+ return;
399
+ }
400
+ if (outcome.warning !== undefined) {
401
+ io.stderr(`[observer] token mint failed: ${outcome.warning}; skipping observer link`);
402
+ return;
403
+ }
404
+ if (outcome.observerUrl !== undefined) io.stdout(`Observer: ${outcome.observerUrl}`);
405
+ }
406
+
146
407
  function emitWait(
147
408
  progress: { runId: string; stepId: string; stepType: string; leaseDeadlineMs: number },
148
409
  io: CliIo,
@@ -155,19 +416,54 @@ function emitWait(
155
416
 
156
417
  function parseArgs(args: readonly string[]): ParsedArgs | undefined {
157
418
  const command = args[0];
419
+ if (command === 'add') return args.length === 2 ? { command: 'add', value: args[1]! } : undefined;
420
+ if (command === 'replay') return parseReplayArgs(args.slice(1));
421
+ if (command === 'build') return parseBuildArgs(args.slice(1));
422
+ if (command === 'deploy') return parseDeployArgs(args.slice(1));
423
+ if (command === 'serve-webhook') return parseWebhookArgs(args.slice(1));
158
424
  if (command === 'hn-monitor') return parseHnMonitorArgs(args.slice(1));
159
425
  if (command === 'tick') return parseTickArgs(args.slice(1));
426
+ if (command === 'observer') return parseObserverArgs(args.slice(1));
160
427
  if (command !== 'check' && command !== 'run' && command !== 'resume') return undefined;
161
428
 
162
429
  let json = false;
430
+ let watch = false;
431
+ let cloud = false;
432
+ let wait = false;
433
+ let localAgent = false;
434
+ let allowHumanInfluenced = false;
163
435
  let dataDir = DEFAULT_DATA_DIR;
164
436
  let sawDataDir = false;
165
437
  let spawn = true;
438
+ let noObserverLink = false;
166
439
  let input: string | undefined;
167
440
  let sawInput = false;
441
+ let reuseFromRunId: string | undefined;
442
+ let bucket: string | undefined;
168
443
  const positionals: string[] = [];
169
444
  for (let index = 1; index < args.length; index += 1) {
170
445
  const argument = args[index]!;
446
+ if (argument === '--cloud' || argument === '--wait') {
447
+ if (command !== 'run' || (argument === '--cloud' ? cloud : wait)) return undefined;
448
+ if (argument === '--cloud') cloud = true;
449
+ else wait = true;
450
+ continue;
451
+ }
452
+ if (argument === '--allow-human-influenced') {
453
+ if (command === 'check' || allowHumanInfluenced) return undefined;
454
+ allowHumanInfluenced = true;
455
+ continue;
456
+ }
457
+ if (argument === '--local-agent') {
458
+ if (command !== 'run' || localAgent) return undefined;
459
+ localAgent = true;
460
+ continue;
461
+ }
462
+ if (argument === '--watch') {
463
+ if (command !== 'check' || watch) return undefined;
464
+ watch = true;
465
+ continue;
466
+ }
171
467
  if (argument === '--json') {
172
468
  if (json) return undefined;
173
469
  json = true;
@@ -180,6 +476,13 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
180
476
  spawn = false;
181
477
  continue;
182
478
  }
479
+ if (argument === '--no-observer-link') {
480
+ // Only meaningful on verbs that actually emit the observer line — `run`
481
+ // and `resume`. Refused elsewhere so the flag never silently no-ops.
482
+ if (command === 'check' || noObserverLink) return undefined;
483
+ noObserverLink = true;
484
+ continue;
485
+ }
183
486
  if (argument === '--data-dir') {
184
487
  const value = args[index + 1];
185
488
  if (command === 'check' || sawDataDir || value === undefined || value.startsWith('-')) return undefined;
@@ -188,6 +491,19 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
188
491
  index += 1;
189
492
  continue;
190
493
  }
494
+ if (argument === '--reuse-from') {
495
+ const value = args[index + 1];
496
+ if (command !== 'run' || reuseFromRunId !== undefined || !value || value.startsWith('-')) return undefined;
497
+ reuseFromRunId = value;
498
+ index += 1;
499
+ continue;
500
+ }
501
+ if (argument === '--bucket') {
502
+ const value = args[++index];
503
+ if (command !== 'run' || bucket !== undefined || !value || value.startsWith('-')) return undefined;
504
+ bucket = value;
505
+ continue;
506
+ }
191
507
  if (argument === '--input') {
192
508
  const value = args[index + 1];
193
509
  if (command !== 'run' || sawInput || value === undefined || value.startsWith('--')) return undefined;
@@ -201,12 +517,24 @@ function parseArgs(args: readonly string[]): ParsedArgs | undefined {
201
517
  }
202
518
  if (positionals.length !== 1) return undefined;
203
519
 
520
+ if (bucket !== undefined && (cloud || !parseDigestReference(positionals[0]!))) return undefined;
521
+ if (cloud) {
522
+ // `--cloud` submits the spec to Cloud, so every flag that only describes a
523
+ // local run -- an inline input, a data dir, a suppressed daemon, a local
524
+ // agent, a local observer-link opt-out -- describes nothing there and is
525
+ // refused rather than ignored.
526
+ if (allowHumanInfluenced || sawInput || sawDataDir || !spawn || localAgent || noObserverLink || reuseFromRunId !== undefined) return undefined;
527
+ return { command: 'cloud-run', value: positionals[0]!, json, wait };
528
+ }
529
+ if (wait) return undefined;
530
+ if (reuseFromRunId !== undefined && isAuthoredFlowPath(positionals[0]!)) return undefined;
531
+
204
532
  if (command === 'run' && input !== undefined && !isAuthoredFlowPath(positionals[0]!)) return undefined;
205
533
  return command === 'check'
206
- ? { command, json, value: positionals[0]! }
534
+ ? { command, json, watch, value: positionals[0]! }
207
535
  : command === 'run'
208
- ? { command, dataDir, input, json, spawn, value: positionals[0]! }
209
- : { command, dataDir, json, spawn, value: positionals[0]! };
536
+ ? { command, bucket, reuseFromRunId, localAgent, dataDir, input, json, spawn, noObserverLink, allowHumanInfluenced, value: positionals[0]! }
537
+ : { command, dataDir, json, spawn, noObserverLink, allowHumanInfluenced, value: positionals[0]! };
210
538
  }
211
539
 
212
540
  function parseHnMonitorArgs(rest: readonly string[]): ParsedArgs | undefined {
@@ -243,6 +571,35 @@ function parseHnMonitorArgs(rest: readonly string[]): ParsedArgs | undefined {
243
571
  return { command: 'hn-monitor', sub: 'start', dataDir, specPath: positionals[0]!, pollIntervalMs };
244
572
  }
245
573
 
574
+ /**
575
+ * `flows observer`. On-demand mint verb: prints an observer URL without
576
+ * running a flow. Takes an optional `--data-dir` (accepted for parity with
577
+ * other verbs, so operators can share one invocation shape across the
578
+ * surface) but does not open a socket, spawn a daemon, or touch the data
579
+ * directory at all -- the mint is a pure Relaycast API round-trip. No
580
+ * positional argument, no other flags.
581
+ */
582
+ function parseObserverArgs(rest: readonly string[]): ParsedArgs | undefined {
583
+ let dataDir = DEFAULT_DATA_DIR;
584
+ let sawDataDir = false;
585
+ for (let index = 0; index < rest.length; index += 1) {
586
+ const argument = rest[index]!;
587
+ if (argument === '--data-dir') {
588
+ const value = rest[index + 1];
589
+ if (sawDataDir || value === undefined || value.startsWith('-')) return undefined;
590
+ dataDir = value;
591
+ sawDataDir = true;
592
+ index += 1;
593
+ continue;
594
+ }
595
+ // Any positional or unknown flag is a shape error: this verb has no
596
+ // spec-path or run-id argument, so silently ignoring extras would be
597
+ // worse than refusing them.
598
+ return undefined;
599
+ }
600
+ return { command: 'observer', dataDir };
601
+ }
602
+
246
603
  /**
247
604
  * `flows tick start`. Numeric flags are parsed here but their BOUNDS are not
248
605
  * re-derived: `runTickRunner` calls `assertTickScheduleValid`, the same
@@ -342,19 +699,41 @@ function emitCheckReport(report: CheckReport, json: boolean, io: CliIo): void {
342
699
  if (report.ok) io.stdout(`CHECK PASSED ${report.path ?? ''}`.trimEnd());
343
700
  }
344
701
 
345
- function emitRunReport(execution: RunExecution, json: boolean, io: CliIo): void {
702
+ function emitRunReport(
703
+ execution: RunExecution,
704
+ json: boolean,
705
+ io: CliIo,
706
+ observerUrl?: string,
707
+ ): void {
346
708
  const { report } = execution;
347
709
  emitDiagnostics(report.diagnostics, io);
348
710
  if (json) {
349
- io.stdout(JSON.stringify(report));
711
+ // Fold `observerUrl` into the JSON report as a sibling of `runId`, so
712
+ // downstream tooling that consumes `--json` gets the same signal a
713
+ // human reader gets from the plain-text `Observer:` line.
714
+ const payload = observerUrl === undefined ? report : { ...report, observerUrl };
715
+ io.stdout(JSON.stringify(payload));
350
716
  return;
351
717
  }
352
718
  if (report.runId === undefined) return;
353
- const completed = report.completedSteps === undefined ? '' : ` (${report.completedSteps} steps)`;
719
+ const completed = report.completedSteps === undefined ? '' : ` (${report.completedSteps} ${report.completedSteps === 1 ? 'step' : 'steps'})`;
354
720
  const reason = report.completionReason === undefined
355
721
  ? ''
356
722
  : ` completionReason: ${report.completionReason}`;
357
723
  io.stdout(`RUN ${report.runId} ${report.status ?? 'unknown'}${completed}${reason}`);
724
+ if (report.reuse !== undefined) {
725
+ io.stdout(`REUSE from ${report.reuse.fromRunId}: ${report.reuse.reusedSteps} reused, ${report.reuse.executedSteps} executed`);
726
+ }
727
+ // The observer line no longer rides inline with the RUN summary in
728
+ // plain-text mode: a slow mint used to hold back this whole line and any
729
+ // check diagnostics for up to `MINT_TIMEOUT_MS`. `finalizeObserverLine`
730
+ // now emits `Observer: <url>` as its own follow-up line after this one,
731
+ // matching v1 relayflows' output shape (which also printed the observer
732
+ // URL on its own line). The `observerUrl` parameter is kept for callers
733
+ // that already have a URL ready and want it inline; today only the
734
+ // `--json` path takes that branch (where the URL folds into the JSON
735
+ // payload above), so the plain-text pass through here never uses it.
736
+ if (observerUrl !== undefined) io.stdout(`Observer: ${observerUrl}`);
358
737
  }
359
738
 
360
739
  function emitDiagnostics(
@@ -0,0 +1,100 @@
1
+ export interface CloudConnectionOptions {
2
+ /** Cloud application base URL; defaults to https://agentrelay.com/cloud. */
3
+ apiUrl?: string;
4
+ /** Scoped Cloud API token; never a Relay workspace key. */
5
+ token?: string;
6
+ signal?: AbortSignal;
7
+ /** Bounds each HTTP request, never the hosted run. Defaults to 30 seconds. */
8
+ requestTimeoutMs?: number;
9
+ }
10
+
11
+ export class CloudFlowError extends Error {
12
+ constructor(
13
+ readonly code: 'configuration' | 'unsupported_source' | 'invalid_input' | 'invalid_response' | 'http_error' | 'transport_error' | 'transient_error',
14
+ message: string,
15
+ readonly status?: number,
16
+ ) {
17
+ super(message);
18
+ this.name = 'CloudFlowError';
19
+ }
20
+ }
21
+
22
+ export function cloudConnection(options: CloudConnectionOptions): { baseUrl: string; token: string } {
23
+ const rawToken = options.token ?? process.env['FLOWS_CLOUD_TOKEN'];
24
+ const token = rawToken?.trim();
25
+ if (!token || /[\r\n]/u.test(rawToken!) || /^(?:rk|ot)_live_/u.test(token)) {
26
+ throw new CloudFlowError('configuration',
27
+ 'Set FLOWS_CLOUD_TOKEN to a scoped Cloud API token (workflow:invoke:write and workflow:runs:read).');
28
+ }
29
+ let url: URL;
30
+ try {
31
+ url = new URL(options.apiUrl ?? process.env['FLOWS_CLOUD_URL'] ?? 'https://agentrelay.com/cloud');
32
+ } catch {
33
+ throw new CloudFlowError('configuration', 'FLOWS_CLOUD_URL must be an absolute Cloud application base URL.');
34
+ }
35
+ if (url.protocol !== 'https:'
36
+ || url.username || url.password || url.search || url.hash || !/^\/[A-Za-z0-9/_-]*$/u.test(url.pathname)) {
37
+ throw new CloudFlowError('configuration', 'Cloud URL must use HTTPS and a plain base path.');
38
+ }
39
+ return { baseUrl: `${url.origin}${url.pathname.replace(/\/+$/u, '')}`, token };
40
+ }
41
+
42
+ export async function cloudRequest(
43
+ path: string,
44
+ options: CloudConnectionOptions,
45
+ body?: unknown,
46
+ ): Promise<unknown> {
47
+ const { baseUrl, token } = cloudConnection(options);
48
+ const timeout = options.requestTimeoutMs ?? 30_000;
49
+ if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 2_147_483_647) {
50
+ throw new CloudFlowError('configuration', 'requestTimeoutMs must be a positive 32-bit integer.');
51
+ }
52
+ const deadline = AbortSignal.timeout(timeout);
53
+ let response: Response;
54
+ try {
55
+ response = await fetch(`${baseUrl}${path}`, {
56
+ method: body === undefined ? 'GET' : 'POST',
57
+ headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
58
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
59
+ signal: options.signal ? AbortSignal.any([options.signal, deadline]) : deadline,
60
+ // A redirect must never carry the credential to a different origin.
61
+ redirect: 'error',
62
+ });
63
+ } catch (error) {
64
+ options.signal?.throwIfAborted();
65
+ throw transportError(deadline.aborted ? deadline.reason : error);
66
+ }
67
+ if (!response.ok) {
68
+ // Do not echo server response bodies: they may contain credentials or source.
69
+ throw new CloudFlowError('http_error', `Cloud request failed with HTTP ${response.status}.`, response.status);
70
+ }
71
+ try {
72
+ return await response.json();
73
+ } catch (error) {
74
+ options.signal?.throwIfAborted();
75
+ if (!(error instanceof SyntaxError)) throw transportError(deadline.aborted ? deadline.reason : error);
76
+ throw new CloudFlowError('invalid_response', 'Cloud returned a non-JSON response.');
77
+ }
78
+ }
79
+
80
+ // Defensive path-segment constraint; accepting a new server ID format needs an SDK change.
81
+ export function cloudRunId(value: unknown): string {
82
+ if (typeof value !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/u.test(value)) {
83
+ throw new CloudFlowError('invalid_response', 'Cloud run ID must contain only letters, digits, underscores, or hyphens.');
84
+ }
85
+ return value;
86
+ }
87
+
88
+ export function isCloudRecord(value: unknown): value is Record<string, unknown> {
89
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
90
+ }
91
+
92
+ function transportError(error: unknown): CloudFlowError {
93
+ const cause = error instanceof Error ? error.cause : undefined;
94
+ const code = isCloudRecord(cause) ? cause.code : undefined;
95
+ const transient = (error instanceof Error && error.name === 'TimeoutError')
96
+ || ['ECONNRESET', 'ECONNREFUSED', 'EAI_AGAIN', 'ETIMEDOUT',
97
+ 'UND_ERR_CONNECT_TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT', 'UND_ERR_BODY_TIMEOUT', 'UND_ERR_SOCKET'].includes(String(code));
98
+ return new CloudFlowError(transient ? 'transient_error' : 'transport_error',
99
+ transient ? 'Cloud transport temporarily unavailable.' : 'Cloud transport failed; check TLS and the configured URL (redirects are refused).');
100
+ }