@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,137 @@
1
+ import { RUN_COMPLETION_REASONS } from '@relayflows/surface';
2
+ import type { RunCompletionReason } from './protocol.js';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { setTimeout } from 'node:timers/promises';
5
+ import { parse as parseYaml } from 'yaml';
6
+ import { canonicalize, specHash } from './canonical.js';
7
+ import { CompileError, compileSpec, kernelToAuthoring, toKernelSpec } from './compile.js';
8
+ import type { FlowSpec } from './spec.js';
9
+ import {
10
+ CloudFlowError, cloudConnection, cloudRequest, cloudRunId, isCloudRecord,
11
+ type CloudConnectionOptions,
12
+ } from './cloud-http.js';
13
+
14
+ export type CloudFlowSource = FlowSpec | { path: string };
15
+ export interface RunInCloudOptions extends CloudConnectionOptions {
16
+ /** Existing Cloud workspace to run in; provisioning/authorization remains server-side. */
17
+ workspaceId?: string;
18
+ }
19
+ export interface CloudRunReceipt {
20
+ runId: string;
21
+ status: 'pending' | 'running';
22
+ /** Local correlation hash of the compiled spec; NOT server attestation or a sealed bundle digest. */
23
+ specHash: string;
24
+ /** Authenticated run API resource; this is not a public sharing URL. */
25
+ apiUrl: string;
26
+ }
27
+ export type CloudRunState =
28
+ | { runId: string; status: 'pending' | 'running' }
29
+ | { runId: string; status: 'completed' | 'failed' | 'cancelled'; completionReason: RunCompletionReason };
30
+
31
+ /**
32
+ * Submit a declarative flow to Cloud's pinned v2 runtime. Compilation only
33
+ * happens here; Cloud owns provisioning and the Rust engine owns execution.
34
+ * Returns acceptance, not completion. No local daemon, CLI probe, or node up.
35
+ */
36
+ export async function runInCloud(
37
+ flow: CloudFlowSource,
38
+ options: RunInCloudOptions = {},
39
+ ): Promise<CloudRunReceipt> {
40
+ const { baseUrl } = cloudConnection(options);
41
+ let spec: FlowSpec;
42
+ try {
43
+ options.signal?.throwIfAborted();
44
+ if ('path' in flow) {
45
+ if (!/\.(?:ya?ml|json)$/iu.test(flow.path)) {
46
+ throw new CloudFlowError('unsupported_source',
47
+ 'Cloud v2 currently accepts declarative YAML or JSON specs; authored TypeScript flows are not supported by the hosted runtime.');
48
+ }
49
+ const parsed: unknown = parseYaml(await readFile(flow.path, 'utf8'));
50
+ try {
51
+ spec = compileSpec(parsed);
52
+ } catch (error) {
53
+ if (!(error instanceof CompileError)) throw error;
54
+ // Prefer the authoring dialect; accept compiled kernel JSON for local-CLI
55
+ // compatibility. If both fail, retain the original authoring diagnostic.
56
+ try {
57
+ spec = compileSpec(kernelToAuthoring(parsed));
58
+ } catch {
59
+ throw error;
60
+ }
61
+ }
62
+ } else {
63
+ spec = compileSpec(flow);
64
+ }
65
+ } catch (error) {
66
+ options.signal?.throwIfAborted();
67
+ if (error instanceof CloudFlowError) throw error;
68
+ throw new CloudFlowError('invalid_input', 'Cannot read or compile the declarative flow. Check the file path and YAML/JSON spec.');
69
+ }
70
+ options.signal?.throwIfAborted();
71
+ const hash = specHash(toKernelSpec(spec));
72
+ const result = await cloudRequest('/api/v1/workflows/run', options, {
73
+ // JSON is a YAML subset. Sending canonical data preserves the exact spec
74
+ // while using the server's existing YAML-to-config admission path.
75
+ workflow: canonicalize(spec),
76
+ fileType: 'yaml',
77
+ relayflowVersion: 'v2',
78
+ ...(options.workspaceId === undefined ? {} : { workspaceId: options.workspaceId }),
79
+ });
80
+ if (!isCloudRecord(result) || (result.status !== 'pending' && result.status !== 'running')) {
81
+ throw new CloudFlowError('invalid_response', 'Cloud did not return an accepted run.');
82
+ }
83
+ const runId = cloudRunId(result.runId);
84
+ return { runId, status: result.status, specHash: hash, apiUrl: `${baseUrl}/api/v1/workflows/runs/${runId}` };
85
+ }
86
+
87
+ export async function getCloudFlowRun(
88
+ runId: string,
89
+ options: CloudConnectionOptions = {},
90
+ ): Promise<CloudRunState> {
91
+ cloudRunId(runId);
92
+ const result = await cloudRequest(`/api/v1/workflows/runs/${runId}`, options);
93
+ if (!isCloudRecord(result) || result.runId !== runId || result.relayflowVersion !== 'v2'
94
+ || typeof result.status !== 'string'
95
+ || !['pending', 'running', 'completed', 'failed', 'cancelled'].includes(result.status)) {
96
+ throw new CloudFlowError('invalid_response', 'Cloud returned an invalid v2 run record.');
97
+ }
98
+ if (result.status === 'pending' || result.status === 'running') return { runId, status: result.status };
99
+ const report = result.result;
100
+ const reason = isCloudRecord(report) ? report.completionReason : undefined;
101
+ if (typeof reason !== 'string' || !(RUN_COMPLETION_REASONS as readonly string[]).includes(reason)
102
+ || (result.status === 'completed' && (reason !== 'success' || !isCloudRecord(report) || report.ok !== true || report.status !== 'completed'))
103
+ || (result.status === 'failed' && !['step_failed', 'budget_exceeded'].includes(reason))
104
+ || (result.status === 'cancelled' && reason !== 'canceled')) {
105
+ throw new CloudFlowError('invalid_response', 'Cloud terminal record lacks a valid, consistent run completionReason; no execution outcome is attested.');
106
+ }
107
+ return { runId, status: result.status as 'completed' | 'failed' | 'cancelled', completionReason: reason as RunCompletionReason };
108
+ }
109
+
110
+ /** Wait without a fixed execution deadline. Abort stops observation, not the hosted run. */
111
+ export async function waitForCloudFlowRun(
112
+ runId: string,
113
+ options: CloudConnectionOptions & { pollIntervalMs?: number } = {},
114
+ ): Promise<CloudRunState> {
115
+ const interval = options.pollIntervalMs ?? 2_000;
116
+ if (!Number.isSafeInteger(interval) || interval < 1 || interval > 60_000) {
117
+ throw new CloudFlowError('configuration', 'pollIntervalMs must be an integer from 1 to 60000.');
118
+ }
119
+ let failures = 0;
120
+ for (;;) {
121
+ options.signal?.throwIfAborted();
122
+ try {
123
+ const run = await getCloudFlowRun(runId, options);
124
+ failures = 0;
125
+ if (run.status !== 'pending' && run.status !== 'running') return run;
126
+ } catch (error) {
127
+ options.signal?.throwIfAborted();
128
+ const transient = error instanceof CloudFlowError && (error.code === 'transient_error'
129
+ || (error.code === 'http_error' && [408, 429, 500, 502, 503, 504].includes(error.status ?? 0)));
130
+ if (!transient) throw error;
131
+ // Only safe GET observation retries. Delay caps at 30s; no overall run deadline.
132
+ failures = Math.min(failures + 1, 16);
133
+ }
134
+ await setTimeout(failures ? Math.min(interval * 2 ** (failures - 1), 30_000) : interval,
135
+ undefined, { signal: options.signal });
136
+ }
137
+ }
package/src/compile.ts CHANGED
@@ -15,6 +15,10 @@
15
15
  // `tests/spec_parity.rs` pin both sides to the same `testdata/` fixture.
16
16
 
17
17
  import { parse as parseYaml } from 'yaml';
18
+ import { parseBudget, toKernelBudget } from './budget.js';
19
+ import { bindingDependencies } from './input-binding.js';
20
+ import { namedGateFailure } from './named-gates.js';
21
+ import { lowerNamedGates } from './named-gate-lowering.js';
18
22
  import type {
19
23
  AgentStepSpec,
20
24
  DeterministicStepSpec,
@@ -38,21 +42,67 @@ import { canonicalize, specHash } from './canonical.js';
38
42
  import { validateOutputDeclaration } from './output-schema.js';
39
43
  import { validateSpec, type ValidationResult } from './validate.js';
40
44
  import { snapshotJsonValue } from './json-value.js';
45
+ import { expandYamlHelpers } from './yaml-helpers.js';
41
46
 
42
47
  export class CompileError extends Error {
43
48
  readonly errors: string[];
44
- constructor(errors: string[]) {
49
+ /** Optional diagnostic kind for callers that classify refusals (e.g. preflight). */
50
+ readonly kind?: string;
51
+ constructor(errors: string[], kind?: string) {
45
52
  super('spec compile failed:\n - ' + errors.join('\n - '));
46
53
  this.name = 'CompileError';
47
54
  this.errors = errors;
55
+ if (kind !== undefined) this.kind = kind;
48
56
  }
49
57
  }
50
58
 
59
+ /**
60
+ * Parse the f.run timeout before submitting any command to the kernel.
61
+ *
62
+ * Accepts a positive whole-millisecond `number`, or a duration string of the
63
+ * form `<coefficient><unit>` where unit is `ms` / `s` / `m` — matched by an
64
+ * anchored regex that captures the coefficient first (group 1) and the unit
65
+ * second (group 2), so a future edit cannot silently swap them. Fractional
66
+ * coefficients that resolve to integer milliseconds are accepted; the
67
+ * multiplication is rounded to the nearest integer so `1.1s → 1100`
68
+ * survives float-precision (`1.1 * 1000 = 1100.0000000000002`) rather than
69
+ * being rejected by `isSafeInteger`.
70
+ */
71
+ export function parseStepTimeout(timeout: unknown): number {
72
+ const unitToMs: Record<'ms' | 's' | 'm', number> = { ms: 1, s: 1000, m: 60_000 };
73
+ let milliseconds: number;
74
+ if (typeof timeout === 'number') {
75
+ milliseconds = timeout;
76
+ } else if (typeof timeout === 'string') {
77
+ const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(timeout);
78
+ if (match === null) {
79
+ milliseconds = NaN;
80
+ } else {
81
+ const coefficient = Number(match[1]);
82
+ const unit = match[2] as 'ms' | 's' | 'm';
83
+ // Round to defeat float precision (1.1 * 1000 = 1100.0000000000002).
84
+ // Fractional milliseconds themselves (e.g. `1.5ms`) still round to `2`,
85
+ // which the isSafeInteger check below accepts. Sub-ms precision is not
86
+ // a supported unit — authors expressing "1.5ms" get the closest int.
87
+ milliseconds = Math.round(coefficient * unitToMs[unit]);
88
+ }
89
+ } else {
90
+ milliseconds = NaN;
91
+ }
92
+ if (!Number.isSafeInteger(milliseconds) || milliseconds <= 0) {
93
+ throw new CompileError(['f.run timeout must be a positive whole number of milliseconds or a duration such as "10s" or "5m".'], 'timeout_invalid');
94
+ }
95
+ if (milliseconds > 15 * 60_000) {
96
+ throw new CompileError(['f.run timeout exceeds the maximum of 15 minutes declared in SURFACE.md.'], 'lease_exceeded');
97
+ }
98
+ return milliseconds;
99
+ }
100
+
51
101
  /**
52
102
  * Compile a YAML string into a validated authoring `FlowSpec`.
53
103
  * Throws `CompileError` on a YAML parse error or any validation failure.
54
104
  */
55
- export function compileYaml(yaml: string): FlowSpec {
105
+ export function compileYaml(yaml: string): CompiledFlowSpec {
56
106
  const parsed = parseYaml(yaml);
57
107
  if (parsed === null || typeof parsed !== 'object') {
58
108
  throw new CompileError(['YAML: expected a mapping at the top level']);
@@ -69,24 +119,43 @@ export function compileYamlToCanonicalJson(yaml: string): string {
69
119
  * Validate a parsed spec object and apply authoring defaults, returning a
70
120
  * normalized `FlowSpec`. Throws `CompileError` on validation failure.
71
121
  */
72
- export function compileSpec(spec: unknown): FlowSpec {
122
+ export type CompiledFlowSpec = Omit<FlowSpec, 'budget'> & { budget?: import('./spec.js').BudgetSpec };
123
+
124
+ export function compileSpec(spec: unknown): CompiledFlowSpec {
73
125
  let snapshot: unknown;
74
126
  try {
75
127
  snapshot = snapshotJsonValue(spec, 'spec');
128
+ snapshot = expandYamlHelpers(snapshot);
76
129
  } catch (error) {
77
130
  throw new CompileError([
78
131
  error instanceof Error ? error.message : 'spec: expected JSON-compatible data',
79
132
  ]);
80
133
  }
134
+ if (snapshot !== null && typeof snapshot === 'object' && !Array.isArray(snapshot) && 'budget' in snapshot && snapshot.budget !== undefined) {
135
+ // parseBudget throws BudgetSyntaxError on any malformed header. Without
136
+ // this wrap, that throw escaped compileSpec's own CompileError contract,
137
+ // so callers (validate, cli/check) that only catch CompileError would
138
+ // surface the budget error as an uncaught exception instead of a
139
+ // diagnostic. Rewrap as CompileError so it flows through the same
140
+ // gate-1 refusal path as every other invalid spec.
141
+ try {
142
+ snapshot = { ...snapshot, budget: parseBudget(snapshot.budget) };
143
+ } catch (error) {
144
+ throw new CompileError(
145
+ [`spec.budget: ${error instanceof Error ? error.message : 'budget_syntax_invalid'}`],
146
+ 'budget_syntax_invalid',
147
+ );
148
+ }
149
+ }
81
150
  const validation: ValidationResult = validateSpec(snapshot);
82
- if (!validation.ok) throw new CompileError(validation.errors);
151
+ if (!validation.ok) throw new CompileError(validation.errors, namedGateFailure(validation.errors));
83
152
 
84
- const input = snapshot as FlowSpec;
153
+ const input = snapshot as CompiledFlowSpec;
85
154
  // Preserve named declarations and selectors through authoring normalization.
86
155
  // They are resolved exactly once at the kernel boundary, after public
87
156
  // preflight has validated every declaration with truthful provenance.
88
157
  const steps = input.steps.map(compileStep);
89
- const flow: FlowSpec = {
158
+ const flow: CompiledFlowSpec = {
90
159
  version: input.version,
91
160
  ...(input.name !== undefined ? { name: input.name } : {}),
92
161
  ...(input.description !== undefined ? { description: input.description } : {}),
@@ -97,6 +166,8 @@ export function compileSpec(spec: unknown): FlowSpec {
97
166
  ...(input.triggers?.length ? { triggers: input.triggers } : {}),
98
167
  steps,
99
168
  ...(input.budget !== undefined ? { budget: input.budget } : {}),
169
+ ...(input.workspace !== undefined ? { workspace: input.workspace } : {}),
170
+ ...(input.tools !== undefined ? { tools: input.tools } : {}),
100
171
  };
101
172
  return flow;
102
173
  }
@@ -107,7 +178,9 @@ function compileStep(step: StepSpec): StepSpec {
107
178
  const base = {
108
179
  id: step.id,
109
180
  type: step.type,
110
- ...(step.dependsOn !== undefined ? { dependsOn: step.dependsOn } : {}),
181
+ ...((step.dependsOn !== undefined || step.input !== undefined)
182
+ ? { dependsOn: step.input === undefined ? step.dependsOn : [...new Set([...(step.dependsOn ?? []), ...bindingDependencies(step.input)])] } : {}),
183
+ ...(step.input !== undefined ? { input: step.input } : {}),
111
184
  maxIterations,
112
185
  ...(step.memory !== undefined ? { memory: step.memory } : {}),
113
186
  ...(step.requirements !== undefined ? { requirements: step.requirements } : {}),
@@ -131,6 +204,7 @@ function compileStep(step: StepSpec): StepSpec {
131
204
  // #138: `timeoutMs` is deterministic-only — worker-backed verbs own
132
205
  // their dispatch timeout. It must be spread HERE and nowhere in `base`.
133
206
  ...(s.timeoutMs !== undefined ? { timeoutMs: s.timeoutMs } : {}),
207
+ ...(s.lease_ms !== undefined ? { lease_ms: parseStepTimeout(s.lease_ms) } : {}),
134
208
  };
135
209
  }
136
210
  case 'llm': {
@@ -158,6 +232,8 @@ function compileStep(step: StepSpec): StepSpec {
158
232
  ...(s.agent !== undefined ? { agent: s.agent } : {}),
159
233
  ...(s.cli !== undefined ? { cli: s.cli } : {}),
160
234
  ...(s.model !== undefined ? { model: s.model } : {}),
235
+ ...(s.cwd !== undefined ? { cwd: s.cwd } : {}),
236
+ ...(s.transport !== undefined ? { transport: s.transport } : {}),
161
237
  recoveryMode,
162
238
  ...(s.surfaces !== undefined ? { surfaces: s.surfaces } : {}),
163
239
  ...(s.permissions !== undefined ? { permissions: s.permissions } : {}),
@@ -252,16 +328,8 @@ export function toKernelSpec(flow: FlowSpec): KernelRunSpec {
252
328
  // reverts the other, and `validateSpec` and `flows check` would both still
253
329
  // look correct. See ops/reviews/20260903-pr139-repair-0903.md section 10.
254
330
  ...(compiled.triggers?.length ? { triggers: compiled.triggers.map(toKernelTrigger) } : {}),
255
- steps: compiled.steps.map((step) => toKernelStep(resolveNamedAgent(step, compiled.agents))),
256
- ...(compiled.budget !== undefined
257
- ? {
258
- budget: {
259
- ...(compiled.budget.maxTokensIn !== undefined ? { max_tokens_in: compiled.budget.maxTokensIn } : {}),
260
- ...(compiled.budget.maxTokensOut !== undefined ? { max_tokens_out: compiled.budget.maxTokensOut } : {}),
261
- ...(compiled.budget.maxDollars !== undefined ? { max_dollars: compiled.budget.maxDollars } : {}),
262
- },
263
- }
264
- : {}),
331
+ steps: lowerNamedGates(compiled.steps).map((step) => toKernelStep(resolveNamedAgent(step, compiled.agents))),
332
+ ...(compiled.budget !== undefined ? { budget: toKernelBudget(compiled.budget) } : {}),
265
333
  };
266
334
  }
267
335
 
@@ -354,15 +422,15 @@ function kernelTriggerToAuthoring(value: unknown, at: string): unknown {
354
422
 
355
423
  function kernelStepToAuthoring(value: unknown, at: string): unknown {
356
424
  const unionKeys = [
357
- 'id', 'type', 'depends_on', 'max_iterations', 'retry', 'verification', 'memory', 'requirements',
358
- 'command', 'timeout_ms', 'prompt', 'model', 'cli', 'instruction',
425
+ 'id', 'type', 'depends_on', 'max_iterations', 'retry', 'verification', 'memory', 'requirements', 'input',
426
+ 'command', 'timeout_ms', 'lease_ms', 'prompt', 'model', 'cli', 'instruction',
359
427
  'recovery_mode', 'surfaces', 'permissions',
360
428
  ] as const;
361
429
  const step = requireKernelObject(value, unionKeys, at);
362
430
  const type = step['type'];
363
- const commonKeys = ['id', 'type', 'depends_on', 'max_iterations', 'retry', 'verification', 'memory', 'requirements'] as const;
431
+ const commonKeys = ['id', 'type', 'depends_on', 'max_iterations', 'retry', 'verification', 'memory', 'requirements', 'input'] as const;
364
432
  const typeKeys = type === 'deterministic'
365
- ? ['command', 'timeout_ms'] as const
433
+ ? ['command', 'timeout_ms', 'lease_ms'] as const
366
434
  : type === 'llm'
367
435
  ? ['prompt', 'model', 'cli'] as const
368
436
  : type === 'agent'
@@ -374,6 +442,7 @@ function kernelStepToAuthoring(value: unknown, at: string): unknown {
374
442
  const common = {
375
443
  id: step['id'],
376
444
  type,
445
+ ...(step['input'] !== undefined ? { input: step['input'] } : {}),
377
446
  ...(step['requirements'] !== undefined ? { requirements: kernelRequirementsToAuthoring(step['requirements'], `${at}.requirements`) } : {}),
378
447
  ...(dependsOn !== undefined && (!Array.isArray(dependsOn) || dependsOn.length > 0)
379
448
  ? { dependsOn }
@@ -387,6 +456,7 @@ function kernelStepToAuthoring(value: unknown, at: string): unknown {
387
456
  ...common,
388
457
  command: step['command'],
389
458
  ...(step['timeout_ms'] !== undefined ? { timeoutMs: step['timeout_ms'] } : {}),
459
+ ...(step['lease_ms'] !== undefined ? { lease_ms: step['lease_ms'] } : {}),
390
460
  };
391
461
  }
392
462
  if (type === 'llm') {
@@ -447,8 +517,12 @@ function kernelMemoryToAuthoring(value: unknown, at: string): unknown {
447
517
  }
448
518
 
449
519
  function kernelBudgetToAuthoring(value: unknown, at: string): unknown {
450
- const budget = requireKernelObject(value, ['max_tokens_in', 'max_tokens_out', 'max_dollars'], at);
520
+ const budget = requireKernelObject(value, ['max_tokens_in', 'max_tokens_out', 'max_dollars', 'max_tokens', 'max_wallclock_ms', 'window', 'pricing'], at);
451
521
  return {
522
+ ...(budget['pricing'] !== undefined ? { pricing: budget['pricing'] } : {}),
523
+ ...(budget['max_tokens'] !== undefined ? { maxTokens: budget['max_tokens'] } : {}),
524
+ ...(budget['max_wallclock_ms'] !== undefined ? { maxWallclockMs: budget['max_wallclock_ms'] } : {}),
525
+ ...(budget['window'] !== undefined ? { window: budget['window'] } : {}),
452
526
  ...(budget['max_tokens_in'] !== undefined ? { maxTokensIn: budget['max_tokens_in'] } : {}),
453
527
  ...(budget['max_tokens_out'] !== undefined ? { maxTokensOut: budget['max_tokens_out'] } : {}),
454
528
  ...(budget['max_dollars'] !== undefined ? { maxDollars: budget['max_dollars'] } : {}),
@@ -505,7 +579,9 @@ function isObject(value: unknown): value is Record<string, unknown> {
505
579
  function toKernelStep(step: StepSpec): KernelStepSpec {
506
580
  const common: KernelStepCommon = {
507
581
  id: step.id,
508
- depends_on: step.dependsOn ?? [],
582
+ depends_on: step.input === undefined ? step.dependsOn ?? []
583
+ : [...new Set([...(step.dependsOn ?? []), ...bindingDependencies(step.input)])],
584
+ ...(step.input !== undefined ? { input: step.input } : {}),
509
585
  max_iterations: step.maxIterations ?? 1,
510
586
  retry: { ...KERNEL_RETRY_DEFAULTS },
511
587
  verification: toKernelVerification(step),
@@ -530,6 +606,7 @@ function toKernelStep(step: StepSpec): KernelStepSpec {
530
606
  type: 'deterministic',
531
607
  command: step.command,
532
608
  ...(step.timeoutMs !== undefined ? { timeout_ms: step.timeoutMs } : {}),
609
+ ...(step.lease_ms !== undefined ? { lease_ms: parseStepTimeout(step.lease_ms) } : {}),
533
610
  };
534
611
  case 'llm': {
535
612
  return {
@@ -547,6 +624,8 @@ function toKernelStep(step: StepSpec): KernelStepSpec {
547
624
  instruction: step.instruction,
548
625
  ...(step.cli !== undefined ? { cli: step.cli } : {}),
549
626
  ...(step.model !== undefined ? { model: step.model } : {}),
627
+ ...(step.cwd !== undefined ? { cwd: step.cwd } : {}),
628
+ ...(step.transport !== undefined ? { transport: step.transport } : {}),
550
629
  recovery_mode: step.recoveryMode ?? 'reset',
551
630
  };
552
631
  const surfaces = {
@@ -0,0 +1,82 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { basename, dirname, join, resolve } from 'node:path';
4
+
5
+ export interface CreateFlowOptions {
6
+ name?: string;
7
+ template?: 'agent' | 'deterministic';
8
+ /** CLI used by future f.agent steps. It must already be authenticated. */
9
+ cli?: string;
10
+ /** Install project dependencies (default true). */
11
+ install?: boolean;
12
+ }
13
+
14
+ export interface CreatedFlow {
15
+ directory: string;
16
+ flowPath: string;
17
+ configPath: string;
18
+ installed: boolean;
19
+ }
20
+
21
+ /** Scaffold a new project. Existing directories are never overwritten. */
22
+ export async function createFlow(target: string, opts: CreateFlowOptions = {}): Promise<CreatedFlow> {
23
+ if (!target.trim()) throw new Error('Choose a new directory, for example: create-flow my-flow');
24
+ const directory = resolve(target);
25
+ const name = opts.name ?? basename(directory);
26
+ if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
27
+ throw new Error('Flow name must be 1–64 lowercase letters, numbers or hyphens, starting with a letter or number.');
28
+ }
29
+ const template = opts.template ?? 'agent';
30
+ if (template !== 'agent' && template !== 'deterministic') throw new Error('Template must be agent or deterministic.');
31
+ const cli = opts.cli ?? 'claude';
32
+ if (!cli.trim() || /[\x00-\x1f\x7f]/.test(cli)) throw new Error('CLI must be a nonempty command or path.');
33
+ const { version } = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8')) as { version: string };
34
+ await mkdir(dirname(directory), { recursive: true });
35
+ try {
36
+ await mkdir(directory);
37
+ } catch (error) {
38
+ if ((error as NodeJS.ErrnoException).code === 'EEXIST') {
39
+ throw new Error(`Directory already exists: ${directory}. Choose a new directory; no files were changed.`);
40
+ }
41
+ throw error;
42
+ }
43
+ const flowFile = `${name}.flow.ts`;
44
+ const agentStep = template === 'agent'
45
+ ? ` const answer = await f.agent('greeter', { task: 'Reply with one short hello sentence. Do not use tools or modify files.' });\n console.log(answer.summary);\n`
46
+ : '';
47
+ const runFlags = template === 'agent' ? ' --local-agent' : '';
48
+ const files: Record<string, string> = {
49
+ [flowFile]: `import { flow } from '@relayflows/surface';\n\nexport default flow(${JSON.stringify(name)}, async (f) => {\n const greeting = await f.run('echo "Hello from Relayflows"');\n console.log(greeting.trim());\n${agentStep} f.done('success');\n});\n`,
50
+ 'flows.json': JSON.stringify({ cli }, null, 2) + '\n',
51
+ 'package.json': JSON.stringify({
52
+ name, private: true, type: 'module',
53
+ scripts: { start: `flows run ${flowFile}${runFlags} --input '{}'` },
54
+ engines: { node: '>=22.18.0' },
55
+ dependencies: { relayflows: version, '@relayflows/surface': version },
56
+ }, null, 2) + '\n',
57
+ '.gitignore': 'node_modules/\n.relayflowd/\n',
58
+ 'README.md': `# ${name}\n\nRun \`npm start\` (or \`npx flows run ${flowFile}${runFlags} --input '{}'\`).\n\n${template === 'agent' ? `Requires ${JSON.stringify(cli)} installed and authenticated. The local agent worker runs\non this machine with the CLI's existing access; it provides no workspace isolation.\n` : 'This starter needs no model credentials.\n'}\n\`flows.json\` selects the CLI for agent steps. Steps execute through the local\njournal; authored TypeScript bodies are not yet durably resumable as a whole.\n`,
59
+ };
60
+ for (const [file, contents] of Object.entries(files)) {
61
+ await writeFile(join(directory, file), contents, { flag: 'wx' });
62
+ }
63
+ if (opts.install !== false) {
64
+ try {
65
+ await installDependencies(directory);
66
+ } catch (error) {
67
+ throw new Error(`Created ${directory}, but dependency installation failed. Run npm install there to retry.`, { cause: error });
68
+ }
69
+ }
70
+ return { directory, flowPath: join(directory, flowFile), configPath: join(directory, 'flows.json'), installed: opts.install !== false };
71
+ }
72
+
73
+ function installDependencies(cwd: string): Promise<void> {
74
+ return new Promise((resolveInstall, reject) => {
75
+ const child = spawn('npm', ['install', '--no-audit', '--no-fund'], { cwd, stdio: 'inherit' });
76
+ child.once('error', reject);
77
+ child.once('close', (code, signal) => {
78
+ if (code === 0) resolveInstall();
79
+ else reject(new Error(`npm install exited ${signal ?? code}`));
80
+ });
81
+ });
82
+ }
@@ -13,6 +13,7 @@
13
13
  // what a file says about itself.
14
14
 
15
15
  import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';
16
+ import { createHash } from 'node:crypto';
16
17
  import {
17
18
  closeSync,
18
19
  mkdirSync,
@@ -20,6 +21,7 @@ import {
20
21
  readFileSync,
21
22
  rmSync,
22
23
  } from 'node:fs';
24
+ import { tmpdir } from 'node:os';
23
25
  import { join, resolve } from 'node:path';
24
26
  import { JournalClient, JournalProtocolError } from './journal-client.js';
25
27
  import { PROTOCOL_VERSION } from './protocol.js';
@@ -145,8 +147,37 @@ export const defaultDaemonLifecycleDeps: DaemonLifecycleDeps = {
145
147
  }),
146
148
  };
147
149
 
150
+ /**
151
+ * Derive the daemon socket path from the data dir.
152
+ *
153
+ * The socket lives outside the data dir at a short hashed path so that a deep
154
+ * working directory cannot push the full socket path past `SUN_LEN` (~104
155
+ * bytes on macOS, ~108 on Linux) — the first-run papercut in #262. Anti-hijack
156
+ * still holds because the daemon derives the same path from the same input;
157
+ * this function and Rust's `crate::socket_path::derive_socket_path` must stay
158
+ * in lockstep. Both use `resolve()` / `std::path::absolute` (lexical, no
159
+ * symlink resolution) so `/var` vs macOS's `/private/var` can never diverge.
160
+ *
161
+ * `SOCKET_FILE` is retained as the exported constant name for compatibility,
162
+ * but is no longer the *entire* socket filename — see the derivation below.
163
+ */
148
164
  export function socketPathFor(dataDir: string): string {
149
- return join(resolve(dataDir), SOCKET_FILE);
165
+ const absolute = resolve(dataDir);
166
+ // 12 hex chars = 6 bytes of SHA-256. Enough separation between data dirs on
167
+ // one machine, small enough to leave headroom under SUN_LEN.
168
+ const hash = createHash('sha256').update(absolute).digest('hex').slice(0, 12);
169
+ return join(runtimeDir(), `relayflowd-${hash}.sock`);
170
+ }
171
+
172
+ function runtimeDir(): string {
173
+ // XDG first (Linux, typically `/run/user/<uid>/`, ~14 chars — the shortest
174
+ // per-user private option). TMPDIR next (macOS, `/var/folders/xx/YYY/T/`,
175
+ // per-user private). Only fall back to os.tmpdir() when neither is set.
176
+ const xdg = process.env['XDG_RUNTIME_DIR'];
177
+ if (xdg && xdg.length > 0) return xdg;
178
+ const tmp = process.env['TMPDIR'];
179
+ if (tmp && tmp.length > 0) return tmp;
180
+ return tmpdir();
150
181
  }
151
182
 
152
183
  export function connectionPathFor(dataDir: string): string {
@@ -1,6 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { dirname, join, resolve } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
+ import { socketPathFor } from './daemon-connection.js';
4
5
  import { pollHackerNewsOnce, type EventSink } from './hn-poller.js';
5
6
  import { JournalClient } from './journal-client.js';
6
7
  import type { EventSubmitResult } from './protocol.js';
@@ -8,7 +9,7 @@ import type { EventSubmitResult } from './protocol.js';
8
9
  const sdkRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
9
10
  const repositoryRoot = resolve(sdkRoot, '..');
10
11
  const dataDir = resolve(process.env.RELAYFLOW_DATA_DIR ?? join(repositoryRoot, '.relayflowd'));
11
- const socketPath = join(dataDir, 'relayflowd.sock');
12
+ const socketPath = socketPathFor(dataDir);
12
13
  const specPath = join(repositoryRoot, 'testdata', 'hn-monitor.spec.canonical.json');
13
14
 
14
15
  interface Submission {
@@ -0,0 +1,97 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { compileSpec, toKernelSpec } from './compile.js';
3
+ import type { JournalClient } from './journal-client.js';
4
+ import type { StepDispatchEvent } from './protocol.js';
5
+ import { SPEC_SCHEMA_VERSION } from './spec.js';
6
+ import { withWorkerLease } from './worker-lease.js';
7
+ import { snapshotJsonValue } from './json-value.js';
8
+
9
+ /** Structural subset of Agent Relay's messages.dm; credentials stay in the client. */
10
+ export interface ChannelBroker {
11
+ messages: {
12
+ dm(input: {
13
+ to: string;
14
+ text: string;
15
+ idempotencyKey: string;
16
+ metadata: { channel: string; messageId: string };
17
+ }): Promise<unknown>;
18
+ };
19
+ }
20
+
21
+ export interface ChannelPost {
22
+ channel: string;
23
+ to: string;
24
+ text: string;
25
+ }
26
+
27
+ function validatePost(post: ChannelPost, participants: readonly string[]): void {
28
+ if (typeof post.channel !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(post.channel)) {
29
+ throw new Error('channel_invalid');
30
+ }
31
+ if (typeof post.to !== 'string' || !post.to.trim() || !participants.includes(post.to)) {
32
+ throw new Error(`channel_participant_unresolved: ${post.to}`);
33
+ }
34
+ if (typeof post.text !== 'string') throw new Error('channel_text_invalid');
35
+ }
36
+
37
+ /** One message boundary per step. Participant resolution precedes journal submission. */
38
+ export function channelPostSpec(
39
+ name: string, stepId: string, post: ChannelPost, participants: readonly string[],
40
+ ) {
41
+ post = snapshotJsonValue(post, 'channel post') as unknown as ChannelPost;
42
+ validatePost(post, participants);
43
+ return toKernelSpec(compileSpec({
44
+ version: SPEC_SCHEMA_VERSION, name,
45
+ steps: [{ id: stepId, type: 'agent',
46
+ instruction: JSON.stringify({ type: 'effect', provider: 'channel', verb: 'post',
47
+ channel: post.channel, to: post.to, text: post.text }),
48
+ maxIterations: 3, recoveryMode: 'reset',
49
+ surfaces: { streams: [{ stream: `channel-${post.channel}` }], external: [`/channel/${post.channel}`] },
50
+ }],
51
+ }));
52
+ }
53
+
54
+ /**
55
+ * Internal post-only proof, for an already provisioned run-scoped broker client.
56
+ * The broker must honor idempotencyKey across worker retries. The application
57
+ * message ID is deliberately distinct from any provider-generated record ID.
58
+ */
59
+ export async function completeChannelPost(
60
+ client: JournalClient, dispatch: StepDispatchEvent, broker: ChannelBroker,
61
+ participants: readonly string[],
62
+ ): Promise<void> {
63
+ const spec = dispatch.spec as { instruction?: string; surfaces?: { external?: string[] } };
64
+ if (dispatch.step_type !== 'agent' || typeof spec.instruction !== 'string') {
65
+ throw new Error('channel_dispatch_invalid');
66
+ }
67
+ const call = JSON.parse(spec.instruction) as ChannelPost & { type: string; provider: string; verb: string };
68
+ if (call.type !== 'effect' || call.provider !== 'channel' || call.verb !== 'post') {
69
+ throw new Error('channel_dispatch_invalid');
70
+ }
71
+ validatePost(call, participants);
72
+ const surfacePath = `/channel/${call.channel}`;
73
+ if (!spec.surfaces?.external?.includes(surfacePath)) throw new Error('channel_surface_undeclared');
74
+ const messageId = createHash('sha256')
75
+ .update(JSON.stringify([dispatch.run_id, dispatch.step_id, call.channel])).digest('hex');
76
+ await withWorkerLease(client, dispatch, async signal => {
77
+ await client.performEffect({
78
+ runId: dispatch.run_id, stepId: dispatch.step_id, attempt: dispatch.attempt,
79
+ idempotencyKey: dispatch.idempotency_key, surfacePath,
80
+ revisionBefore: 'pending', revisionAfter: messageId,
81
+ }, async () => {
82
+ signal.throwIfAborted();
83
+ await broker.messages.dm({ to: call.to, text: call.text, idempotencyKey: messageId,
84
+ metadata: { channel: call.channel, messageId } });
85
+ signal.throwIfAborted();
86
+ });
87
+ });
88
+ // All receipt fields already exist in the journaled spec and stable run identity.
89
+ // A crash after confirm needs neither a local receipt cache nor a broker read.
90
+ await client.stepComplete(dispatch.run_id, dispatch.step_id, dispatch.attempt,
91
+ dispatch.idempotency_key, 'success', {
92
+ output: { type: 'effect', provider: 'channel', verb: 'post',
93
+ messageId, channel: call.channel, to: call.to, text: call.text },
94
+ started_pins: dispatch.pins, end_pins: dispatch.pins,
95
+ effects: [{ surface_path: surfacePath, idempotency_key: dispatch.idempotency_key }],
96
+ });
97
+ }