@relayflows/sdk 2.0.9 → 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 (475) 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 +2 -21
  30. package/dist/authored-flow-error.d.ts.map +1 -1
  31. package/dist/authored-flow-error.js +1 -12
  32. package/dist/authored-flow-error.js.map +1 -1
  33. package/dist/authored-flow-executor.d.ts +13 -7
  34. package/dist/authored-flow-executor.d.ts.map +1 -1
  35. package/dist/authored-flow-executor.js +116 -235
  36. package/dist/authored-flow-executor.js.map +1 -1
  37. package/dist/authored-flow-loader.d.ts +10 -1
  38. package/dist/authored-flow-loader.d.ts.map +1 -1
  39. package/dist/authored-flow-loader.js +56 -3
  40. package/dist/authored-flow-loader.js.map +1 -1
  41. package/dist/authored-flow-operation.d.ts +9 -1
  42. package/dist/authored-flow-operation.d.ts.map +1 -1
  43. package/dist/authored-flow-operation.js +35 -4
  44. package/dist/authored-flow-operation.js.map +1 -1
  45. package/dist/authored-helper-effect.d.ts +12 -0
  46. package/dist/authored-helper-effect.d.ts.map +1 -0
  47. package/dist/authored-helper-effect.js +144 -0
  48. package/dist/authored-helper-effect.js.map +1 -0
  49. package/dist/authored-mcp.d.ts +17 -0
  50. package/dist/authored-mcp.d.ts.map +1 -0
  51. package/dist/authored-mcp.js +171 -0
  52. package/dist/authored-mcp.js.map +1 -0
  53. package/dist/authored-memory.d.ts +7 -0
  54. package/dist/authored-memory.d.ts.map +1 -0
  55. package/dist/authored-memory.js +60 -0
  56. package/dist/authored-memory.js.map +1 -0
  57. package/dist/authored-plugin-effect.d.ts +16 -0
  58. package/dist/authored-plugin-effect.d.ts.map +1 -0
  59. package/dist/authored-plugin-effect.js +163 -0
  60. package/dist/authored-plugin-effect.js.map +1 -0
  61. package/dist/authored-step-output.d.ts +23 -0
  62. package/dist/authored-step-output.d.ts.map +1 -0
  63. package/dist/authored-step-output.js +67 -0
  64. package/dist/authored-step-output.js.map +1 -0
  65. package/dist/authored-worker-step.d.ts +15 -0
  66. package/dist/authored-worker-step.d.ts.map +1 -0
  67. package/dist/authored-worker-step.js +136 -0
  68. package/dist/authored-worker-step.js.map +1 -0
  69. package/dist/budget-preflight.d.ts +5 -0
  70. package/dist/budget-preflight.d.ts.map +1 -0
  71. package/dist/budget-preflight.js +33 -0
  72. package/dist/budget-preflight.js.map +1 -0
  73. package/dist/budget.d.ts +14 -0
  74. package/dist/budget.d.ts.map +1 -0
  75. package/dist/budget.js +52 -0
  76. package/dist/budget.js.map +1 -0
  77. package/dist/bundle-transport.d.ts +18 -0
  78. package/dist/bundle-transport.d.ts.map +1 -0
  79. package/dist/bundle-transport.js +111 -0
  80. package/dist/bundle-transport.js.map +1 -0
  81. package/dist/bundle-typescript.d.ts +8 -0
  82. package/dist/bundle-typescript.d.ts.map +1 -0
  83. package/dist/bundle-typescript.js +108 -0
  84. package/dist/bundle-typescript.js.map +1 -0
  85. package/dist/bundle.d.ts +25 -0
  86. package/dist/bundle.d.ts.map +1 -0
  87. package/dist/bundle.js +199 -0
  88. package/dist/bundle.js.map +1 -0
  89. package/dist/child-stop.d.ts +59 -0
  90. package/dist/child-stop.d.ts.map +1 -0
  91. package/dist/child-stop.js +94 -0
  92. package/dist/child-stop.js.map +1 -0
  93. package/dist/cli/add.d.ts +6 -0
  94. package/dist/cli/add.d.ts.map +1 -0
  95. package/dist/cli/add.js +47 -0
  96. package/dist/cli/add.js.map +1 -0
  97. package/dist/cli/build.d.ts +13 -0
  98. package/dist/cli/build.d.ts.map +1 -0
  99. package/dist/cli/build.js +224 -0
  100. package/dist/cli/build.js.map +1 -0
  101. package/dist/cli/bundle-preflight.d.ts +5 -0
  102. package/dist/cli/bundle-preflight.d.ts.map +1 -0
  103. package/dist/cli/bundle-preflight.js +28 -0
  104. package/dist/cli/bundle-preflight.js.map +1 -0
  105. package/dist/cli/check-helper-body.d.ts +4 -0
  106. package/dist/cli/check-helper-body.d.ts.map +1 -0
  107. package/dist/cli/check-helper-body.js +15 -0
  108. package/dist/cli/check-helper-body.js.map +1 -0
  109. package/dist/cli/check-triggers.d.ts +14 -0
  110. package/dist/cli/check-triggers.d.ts.map +1 -0
  111. package/dist/cli/check-triggers.js +37 -0
  112. package/dist/cli/check-triggers.js.map +1 -0
  113. package/dist/cli/check-typescript.d.ts +20 -0
  114. package/dist/cli/check-typescript.d.ts.map +1 -0
  115. package/dist/cli/check-typescript.js +54 -0
  116. package/dist/cli/check-typescript.js.map +1 -0
  117. package/dist/cli/check.d.ts +36 -3
  118. package/dist/cli/check.d.ts.map +1 -1
  119. package/dist/cli/check.js +122 -11
  120. package/dist/cli/check.js.map +1 -1
  121. package/dist/cli/cloud-run.d.ts +8 -0
  122. package/dist/cli/cloud-run.d.ts.map +1 -0
  123. package/dist/cli/cloud-run.js +50 -0
  124. package/dist/cli/cloud-run.js.map +1 -0
  125. package/dist/cli/deploy.d.ts +9 -0
  126. package/dist/cli/deploy.d.ts.map +1 -0
  127. package/dist/cli/deploy.js +58 -0
  128. package/dist/cli/deploy.js.map +1 -0
  129. package/dist/cli/deterministic-failure.d.ts +5 -0
  130. package/dist/cli/deterministic-failure.d.ts.map +1 -0
  131. package/dist/cli/deterministic-failure.js +66 -0
  132. package/dist/cli/deterministic-failure.js.map +1 -0
  133. package/dist/cli/direct-run.d.ts.map +1 -1
  134. package/dist/cli/direct-run.js +83 -18
  135. package/dist/cli/direct-run.js.map +1 -1
  136. package/dist/cli/hn-monitor.d.ts.map +1 -1
  137. package/dist/cli/hn-monitor.js +2 -1
  138. package/dist/cli/hn-monitor.js.map +1 -1
  139. package/dist/cli/replay.d.ts +12 -0
  140. package/dist/cli/replay.d.ts.map +1 -0
  141. package/dist/cli/replay.js +82 -0
  142. package/dist/cli/replay.js.map +1 -0
  143. package/dist/cli/reuse.d.ts +8 -0
  144. package/dist/cli/reuse.d.ts.map +1 -0
  145. package/dist/cli/reuse.js +25 -0
  146. package/dist/cli/reuse.js.map +1 -0
  147. package/dist/cli/run-digest.d.ts +4 -0
  148. package/dist/cli/run-digest.d.ts.map +1 -0
  149. package/dist/cli/run-digest.js +24 -0
  150. package/dist/cli/run-digest.js.map +1 -0
  151. package/dist/cli/run.d.ts +16 -5
  152. package/dist/cli/run.d.ts.map +1 -1
  153. package/dist/cli/run.js +99 -17
  154. package/dist/cli/run.js.map +1 -1
  155. package/dist/cli/serve-webhook.d.ts +40 -0
  156. package/dist/cli/serve-webhook.d.ts.map +1 -0
  157. package/dist/cli/serve-webhook.js +233 -0
  158. package/dist/cli/serve-webhook.js.map +1 -0
  159. package/dist/cli/tick-runner.d.ts.map +1 -1
  160. package/dist/cli/tick-runner.js +2 -1
  161. package/dist/cli/tick-runner.js.map +1 -1
  162. package/dist/cli-adapter.d.ts +8 -14
  163. package/dist/cli-adapter.d.ts.map +1 -1
  164. package/dist/cli-adapter.js +19 -64
  165. package/dist/cli-adapter.js.map +1 -1
  166. package/dist/cli-watch.d.ts +13 -0
  167. package/dist/cli-watch.d.ts.map +1 -0
  168. package/dist/cli-watch.js +185 -0
  169. package/dist/cli-watch.js.map +1 -0
  170. package/dist/cli.d.ts +4 -0
  171. package/dist/cli.d.ts.map +1 -1
  172. package/dist/cli.js +387 -14
  173. package/dist/cli.js.map +1 -1
  174. package/dist/cloud-http.d.ts +22 -0
  175. package/dist/cloud-http.d.ts.map +1 -0
  176. package/dist/cloud-http.js +84 -0
  177. package/dist/cloud-http.js.map +1 -0
  178. package/dist/cloud-run.d.ts +38 -0
  179. package/dist/cloud-run.d.ts.map +1 -0
  180. package/dist/cloud-run.js +112 -0
  181. package/dist/cloud-run.js.map +1 -0
  182. package/dist/compile.d.ts +21 -3
  183. package/dist/compile.d.ts.map +1 -1
  184. package/dist/compile.js +97 -23
  185. package/dist/compile.js.map +1 -1
  186. package/dist/create-flow.d.ts +17 -0
  187. package/dist/create-flow.d.ts.map +1 -0
  188. package/dist/create-flow.js +72 -0
  189. package/dist/create-flow.js.map +1 -0
  190. package/dist/daemon-connection.d.ts +14 -0
  191. package/dist/daemon-connection.d.ts.map +1 -1
  192. package/dist/daemon-connection.js +33 -1
  193. package/dist/daemon-connection.js.map +1 -1
  194. package/dist/demo-hn-monitor.js +2 -1
  195. package/dist/demo-hn-monitor.js.map +1 -1
  196. package/dist/effect-channel.d.ts +30 -0
  197. package/dist/effect-channel.d.ts.map +1 -0
  198. package/dist/effect-channel.js +71 -0
  199. package/dist/effect-channel.js.map +1 -0
  200. package/dist/failure-kinds.d.ts +14 -3
  201. package/dist/failure-kinds.d.ts.map +1 -1
  202. package/dist/failure-kinds.js +27 -0
  203. package/dist/failure-kinds.js.map +1 -1
  204. package/dist/gate-contract.d.ts.map +1 -1
  205. package/dist/gate-contract.js +9 -0
  206. package/dist/gate-contract.js.map +1 -1
  207. package/dist/helper-preflight.d.ts +18 -0
  208. package/dist/helper-preflight.d.ts.map +1 -0
  209. package/dist/helper-preflight.js +32 -0
  210. package/dist/helper-preflight.js.map +1 -0
  211. package/dist/helper-storage.d.ts +5 -0
  212. package/dist/helper-storage.d.ts.map +1 -0
  213. package/dist/helper-storage.js +31 -0
  214. package/dist/helper-storage.js.map +1 -0
  215. package/dist/helper-writeback.d.ts +12 -0
  216. package/dist/helper-writeback.d.ts.map +1 -0
  217. package/dist/helper-writeback.js +95 -0
  218. package/dist/helper-writeback.js.map +1 -0
  219. package/dist/index.d.ts +10 -2
  220. package/dist/index.d.ts.map +1 -1
  221. package/dist/index.js +7 -1
  222. package/dist/index.js.map +1 -1
  223. package/dist/input-binding.d.ts +4 -0
  224. package/dist/input-binding.d.ts.map +1 -0
  225. package/dist/input-binding.js +78 -0
  226. package/dist/input-binding.js.map +1 -0
  227. package/dist/journal-client.d.ts +12 -4
  228. package/dist/journal-client.d.ts.map +1 -1
  229. package/dist/journal-client.js +17 -6
  230. package/dist/journal-client.js.map +1 -1
  231. package/dist/journal-offset.d.ts +3 -0
  232. package/dist/journal-offset.d.ts.map +1 -0
  233. package/dist/journal-offset.js +69 -0
  234. package/dist/journal-offset.js.map +1 -0
  235. package/dist/journal-reader.d.ts +30 -0
  236. package/dist/journal-reader.d.ts.map +1 -0
  237. package/dist/journal-reader.js +156 -0
  238. package/dist/journal-reader.js.map +1 -0
  239. package/dist/json-schema.d.ts +2 -0
  240. package/dist/json-schema.d.ts.map +1 -1
  241. package/dist/json-schema.js +38 -22
  242. package/dist/json-schema.js.map +1 -1
  243. package/dist/llm-worker.d.ts +16 -0
  244. package/dist/llm-worker.d.ts.map +1 -0
  245. package/dist/llm-worker.js +79 -0
  246. package/dist/llm-worker.js.map +1 -0
  247. package/dist/local-agent.d.ts +8 -0
  248. package/dist/local-agent.d.ts.map +1 -0
  249. package/dist/local-agent.js +25 -0
  250. package/dist/local-agent.js.map +1 -0
  251. package/dist/mcp-client.d.ts +15 -0
  252. package/dist/mcp-client.d.ts.map +1 -0
  253. package/dist/mcp-client.js +104 -0
  254. package/dist/mcp-client.js.map +1 -0
  255. package/dist/mcp-config.d.ts +4 -0
  256. package/dist/mcp-config.d.ts.map +1 -0
  257. package/dist/mcp-config.js +61 -0
  258. package/dist/mcp-config.js.map +1 -0
  259. package/dist/mcp-stdio.d.ts +24 -0
  260. package/dist/mcp-stdio.d.ts.map +1 -0
  261. package/dist/mcp-stdio.js +89 -0
  262. package/dist/mcp-stdio.js.map +1 -0
  263. package/dist/model-pricing.d.ts +22 -0
  264. package/dist/model-pricing.d.ts.map +1 -0
  265. package/dist/model-pricing.js +30 -0
  266. package/dist/model-pricing.js.map +1 -0
  267. package/dist/mount-registry.d.ts +9 -0
  268. package/dist/mount-registry.d.ts.map +1 -0
  269. package/dist/mount-registry.js +47 -0
  270. package/dist/mount-registry.js.map +1 -0
  271. package/dist/named-gate-lowering.d.ts +9 -0
  272. package/dist/named-gate-lowering.d.ts.map +1 -0
  273. package/dist/named-gate-lowering.js +152 -0
  274. package/dist/named-gate-lowering.js.map +1 -0
  275. package/dist/named-gates.d.ts +10 -0
  276. package/dist/named-gates.d.ts.map +1 -0
  277. package/dist/named-gates.js +69 -0
  278. package/dist/named-gates.js.map +1 -0
  279. package/dist/observer-link.d.ts +129 -0
  280. package/dist/observer-link.d.ts.map +1 -0
  281. package/dist/observer-link.js +274 -0
  282. package/dist/observer-link.js.map +1 -0
  283. package/dist/plugin-loader.d.ts +13 -0
  284. package/dist/plugin-loader.d.ts.map +1 -0
  285. package/dist/plugin-loader.js +110 -0
  286. package/dist/plugin-loader.js.map +1 -0
  287. package/dist/plugin-manifest.d.ts +27 -0
  288. package/dist/plugin-manifest.d.ts.map +1 -0
  289. package/dist/plugin-manifest.js +89 -0
  290. package/dist/plugin-manifest.js.map +1 -0
  291. package/dist/preflight.d.ts +26 -2
  292. package/dist/preflight.d.ts.map +1 -1
  293. package/dist/preflight.js +175 -1
  294. package/dist/preflight.js.map +1 -1
  295. package/dist/progress.d.ts +14 -0
  296. package/dist/progress.d.ts.map +1 -0
  297. package/dist/progress.js +39 -0
  298. package/dist/progress.js.map +1 -0
  299. package/dist/protocol.d.ts +11 -0
  300. package/dist/protocol.d.ts.map +1 -1
  301. package/dist/pty-sidechannel.d.ts +14 -0
  302. package/dist/pty-sidechannel.d.ts.map +1 -0
  303. package/dist/pty-sidechannel.js +103 -0
  304. package/dist/pty-sidechannel.js.map +1 -0
  305. package/dist/scope-compiler.d.ts +52 -0
  306. package/dist/scope-compiler.d.ts.map +1 -0
  307. package/dist/scope-compiler.js +62 -0
  308. package/dist/scope-compiler.js.map +1 -0
  309. package/dist/slack-preflight.d.ts +6 -0
  310. package/dist/slack-preflight.d.ts.map +1 -0
  311. package/dist/slack-preflight.js +28 -0
  312. package/dist/slack-preflight.js.map +1 -0
  313. package/dist/slack-writeback.d.ts +40 -0
  314. package/dist/slack-writeback.d.ts.map +1 -0
  315. package/dist/slack-writeback.js +60 -0
  316. package/dist/slack-writeback.js.map +1 -0
  317. package/dist/spec.d.ts +134 -4
  318. package/dist/spec.d.ts.map +1 -1
  319. package/dist/spec.js.map +1 -1
  320. package/dist/step-dependencies.d.ts.map +1 -1
  321. package/dist/step-dependencies.js +2 -1
  322. package/dist/step-dependencies.js.map +1 -1
  323. package/dist/step-fields.d.ts +4 -4
  324. package/dist/step-fields.d.ts.map +1 -1
  325. package/dist/step-fields.js +5 -2
  326. package/dist/step-fields.js.map +1 -1
  327. package/dist/trigger-executor.d.ts +7 -0
  328. package/dist/trigger-executor.d.ts.map +1 -0
  329. package/dist/trigger-executor.js +35 -0
  330. package/dist/trigger-executor.js.map +1 -0
  331. package/dist/validate.d.ts.map +1 -1
  332. package/dist/validate.js +53 -6
  333. package/dist/validate.js.map +1 -1
  334. package/dist/webhook-rate-limit.d.ts +29 -0
  335. package/dist/webhook-rate-limit.d.ts.map +1 -0
  336. package/dist/webhook-rate-limit.js +38 -0
  337. package/dist/webhook-rate-limit.js.map +1 -0
  338. package/dist/webhook-signature.d.ts +22 -0
  339. package/dist/webhook-signature.d.ts.map +1 -0
  340. package/dist/webhook-signature.js +53 -0
  341. package/dist/webhook-signature.js.map +1 -0
  342. package/dist/worker-cli.d.ts +13 -1
  343. package/dist/worker-cli.d.ts.map +1 -1
  344. package/dist/worker-cli.js +133 -12
  345. package/dist/worker-cli.js.map +1 -1
  346. package/dist/worker-input.d.ts +4 -0
  347. package/dist/worker-input.d.ts.map +1 -0
  348. package/dist/worker-input.js +14 -0
  349. package/dist/worker-input.js.map +1 -0
  350. package/dist/worker-lease.d.ts +5 -0
  351. package/dist/worker-lease.d.ts.map +1 -0
  352. package/dist/worker-lease.js +80 -0
  353. package/dist/worker-lease.js.map +1 -0
  354. package/dist/worker-spend.d.ts +19 -0
  355. package/dist/worker-spend.d.ts.map +1 -0
  356. package/dist/worker-spend.js +22 -0
  357. package/dist/worker-spend.js.map +1 -0
  358. package/dist/worker-usage.d.ts +7 -0
  359. package/dist/worker-usage.d.ts.map +1 -0
  360. package/dist/worker-usage.js +54 -0
  361. package/dist/worker-usage.js.map +1 -0
  362. package/dist/worker.d.ts +2 -0
  363. package/dist/worker.d.ts.map +1 -1
  364. package/dist/worker.js +22 -8
  365. package/dist/worker.js.map +1 -1
  366. package/dist/wrapper-session.d.ts +1 -1
  367. package/dist/wrapper-session.d.ts.map +1 -1
  368. package/dist/wrapper-session.js +56 -19
  369. package/dist/wrapper-session.js.map +1 -1
  370. package/dist/yaml-helper-effect.d.ts +8 -0
  371. package/dist/yaml-helper-effect.d.ts.map +1 -0
  372. package/dist/yaml-helper-effect.js +79 -0
  373. package/dist/yaml-helper-effect.js.map +1 -0
  374. package/dist/yaml-helpers.d.ts +18 -0
  375. package/dist/yaml-helpers.d.ts.map +1 -0
  376. package/dist/yaml-helpers.js +116 -0
  377. package/dist/yaml-helpers.js.map +1 -0
  378. package/package.json +19 -2
  379. package/src/adapters/base.ts +53 -0
  380. package/src/adapters/claude.ts +55 -0
  381. package/src/adapters/codex.ts +56 -0
  382. package/src/adapters/index.ts +36 -0
  383. package/src/adapters/wrapper.ts +42 -0
  384. package/src/agent-relay-transport.ts +187 -0
  385. package/src/authored-budget.ts +69 -0
  386. package/src/authored-flow-error.ts +10 -10
  387. package/src/authored-flow-executor.ts +171 -347
  388. package/src/authored-flow-loader.ts +60 -3
  389. package/src/authored-flow-operation.ts +38 -5
  390. package/src/authored-helper-effect.ts +143 -0
  391. package/src/authored-mcp.ts +166 -0
  392. package/src/authored-memory.ts +55 -0
  393. package/src/authored-plugin-effect.ts +152 -0
  394. package/src/authored-step-output.ts +107 -0
  395. package/src/authored-worker-step.ts +181 -0
  396. package/src/budget-preflight.ts +31 -0
  397. package/src/budget.ts +49 -0
  398. package/src/bundle-transport.ts +79 -0
  399. package/src/bundle-typescript.ts +98 -0
  400. package/src/bundle.ts +182 -0
  401. package/src/child-stop.ts +138 -0
  402. package/src/cli/add.ts +45 -0
  403. package/src/cli/build.ts +214 -0
  404. package/src/cli/bundle-preflight.ts +28 -0
  405. package/src/cli/check-helper-body.ts +14 -0
  406. package/src/cli/check-triggers.ts +41 -0
  407. package/src/cli/check-typescript.ts +63 -0
  408. package/src/cli/check.ts +132 -14
  409. package/src/cli/cloud-run.ts +47 -0
  410. package/src/cli/deploy.ts +52 -0
  411. package/src/cli/deterministic-failure.ts +69 -0
  412. package/src/cli/direct-run.ts +79 -18
  413. package/src/cli/hn-monitor.ts +2 -1
  414. package/src/cli/replay.ts +81 -0
  415. package/src/cli/reuse.ts +23 -0
  416. package/src/cli/run-digest.ts +24 -0
  417. package/src/cli/run.ts +104 -21
  418. package/src/cli/serve-webhook.ts +246 -0
  419. package/src/cli/tick-runner.ts +2 -1
  420. package/src/cli-adapter.ts +31 -74
  421. package/src/cli-watch.ts +174 -0
  422. package/src/cli.ts +396 -17
  423. package/src/cloud-http.ts +100 -0
  424. package/src/cloud-run.ts +137 -0
  425. package/src/compile.ts +102 -23
  426. package/src/create-flow.ts +82 -0
  427. package/src/daemon-connection.ts +32 -1
  428. package/src/demo-hn-monitor.ts +2 -1
  429. package/src/effect-channel.ts +97 -0
  430. package/src/failure-kinds.ts +38 -0
  431. package/src/gate-contract.ts +7 -0
  432. package/src/helper-preflight.ts +34 -0
  433. package/src/helper-storage.ts +23 -0
  434. package/src/helper-writeback.ts +83 -0
  435. package/src/index.ts +26 -0
  436. package/src/input-binding.ts +76 -0
  437. package/src/journal-client.ts +20 -7
  438. package/src/journal-offset.ts +60 -0
  439. package/src/journal-reader.ts +165 -0
  440. package/src/json-schema.ts +34 -18
  441. package/src/llm-worker.ts +82 -0
  442. package/src/local-agent.ts +30 -0
  443. package/src/mcp-client.ts +97 -0
  444. package/src/mcp-config.ts +42 -0
  445. package/src/mcp-stdio.ts +84 -0
  446. package/src/model-pricing.ts +32 -0
  447. package/src/mount-registry.ts +44 -0
  448. package/src/named-gate-lowering.ts +147 -0
  449. package/src/named-gates.ts +76 -0
  450. package/src/observer-link.ts +368 -0
  451. package/src/plugin-loader.ts +88 -0
  452. package/src/plugin-manifest.ts +76 -0
  453. package/src/preflight.ts +178 -4
  454. package/src/progress.ts +51 -0
  455. package/src/protocol.ts +12 -0
  456. package/src/pty-sidechannel.ts +92 -0
  457. package/src/scope-compiler.ts +105 -0
  458. package/src/slack-preflight.ts +25 -0
  459. package/src/slack-writeback.ts +63 -0
  460. package/src/spec.ts +112 -4
  461. package/src/step-dependencies.ts +3 -1
  462. package/src/step-fields.ts +5 -2
  463. package/src/trigger-executor.ts +34 -0
  464. package/src/validate.ts +47 -6
  465. package/src/webhook-rate-limit.ts +69 -0
  466. package/src/webhook-signature.ts +74 -0
  467. package/src/worker-cli.ts +156 -11
  468. package/src/worker-input.ts +15 -0
  469. package/src/worker-lease.ts +85 -0
  470. package/src/worker-spend.ts +23 -0
  471. package/src/worker-usage.ts +48 -0
  472. package/src/worker.ts +27 -9
  473. package/src/wrapper-session.ts +50 -15
  474. package/src/yaml-helper-effect.ts +78 -0
  475. package/src/yaml-helpers.ts +131 -0
@@ -0,0 +1,76 @@
1
+ import { Ajv } from 'ajv';
2
+ import { snapshotJsonValue } from './json-value.js';
3
+
4
+ export const PLUGIN_FAILURE_KINDS = [
5
+ 'plugin_unknown', 'plugin_unlisted', 'plugin_install_failed', 'plugin_manifest_missing',
6
+ 'plugin_manifest_invalid', 'plugin_preflight_missing', 'plugin_verb_unknown_primitive',
7
+ 'plugin_unsupported', 'plugin_credential_missing', 'plugin_server_unreachable',
8
+ ] as const;
9
+ export type PluginFailureKind = typeof PLUGIN_FAILURE_KINDS[number];
10
+ export class PluginError extends Error {
11
+ constructor(readonly code: PluginFailureKind, message: string) { super(message); }
12
+ }
13
+ export interface PluginVerb {
14
+ namespace: string;
15
+ method: string;
16
+ lowersTo: 'run' | 'llm' | 'agent' | 'effect' | 'wait';
17
+ args: Record<string, unknown>;
18
+ }
19
+ export interface PluginManifest {
20
+ name: string;
21
+ version: string;
22
+ verbs: readonly PluginVerb[];
23
+ triggers: readonly unknown[];
24
+ gates: readonly unknown[];
25
+ preflight: { credentials: readonly string[]; servers: readonly string[] };
26
+ }
27
+ const identifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
28
+ const reserved = new Set(['run', 'llm', 'agent', 'human', 'dispatch', 'done', 'cloud', 'memory', 'mcp', 'slack', '__proto__', 'constructor', 'prototype', 'then']);
29
+ const object = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
30
+ const strings = (v: unknown): v is string[] => Array.isArray(v) && v.every(s => typeof s === 'string' && s.trim().length > 0);
31
+ export function pluginPackageName(name: string): string {
32
+ const bare = name.replace(/^@flows\//, '');
33
+ if (!/^helper-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(bare)) {
34
+ throw new PluginError('plugin_manifest_invalid', 'Expected helper-<name> or @flows/helper-<name>.');
35
+ }
36
+ return `@flows/${bare}`;
37
+ }
38
+ export function validatePluginManifest(input: unknown, packageName?: string): PluginManifest {
39
+ let v: unknown;
40
+ try { v = snapshotJsonValue(input, 'plugin manifest'); }
41
+ catch { throw new PluginError('plugin_manifest_invalid', 'Plugin manifest must be JSON data.'); }
42
+ const invalid = (message: string): never => { throw new PluginError('plugin_manifest_invalid', message); };
43
+ if (!object(v)) return invalid('Expected a plugin manifest object.');
44
+ if (!Object.hasOwn(v, 'preflight')) throw new PluginError('plugin_preflight_missing', 'Plugin must declare preflight.');
45
+ if (typeof v.name !== 'string' || typeof v.version !== 'string' || !v.version.trim()) return invalid('Plugin name and version are required.');
46
+ const resolved = pluginPackageName(v.name);
47
+ if (packageName !== undefined && resolved !== packageName) return invalid('Plugin name does not match its package.');
48
+ if (!Array.isArray(v.verbs) || !Array.isArray(v.triggers) || !Array.isArray(v.gates)) return invalid('Expected verbs, triggers, and gates arrays.');
49
+ if (!object(v.preflight) || !strings(v.preflight.credentials) || !strings(v.preflight.servers)) return invalid('Preflight requires credentials and servers arrays.');
50
+ if (!v.preflight.credentials.every(s => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s))) return invalid('Invalid credential environment variable.');
51
+ for (const server of v.preflight.servers) {
52
+ try { if (!['http:', 'https:'].includes(new URL(server).protocol)) return invalid('Servers must be HTTP(S) URLs.'); }
53
+ catch { return invalid('Servers must be HTTP(S) URLs.'); }
54
+ }
55
+ const seen = new Set<string>();
56
+ const ajv = new Ajv({ strict: false });
57
+ for (const verb of v.verbs) {
58
+ if (!object(verb) || typeof verb.namespace !== 'string' || typeof verb.method !== 'string'
59
+ || !identifier.test(verb.namespace) || !identifier.test(verb.method)
60
+ || reserved.has(verb.namespace) || ['__proto__', 'constructor', 'prototype', 'then'].includes(verb.method)) return invalid('Invalid or reserved plugin verb.');
61
+ if (!['run', 'llm', 'agent', 'effect', 'wait'].includes(String(verb.lowersTo))) {
62
+ throw new PluginError('plugin_verb_unknown_primitive', `Unknown primitive for ${verb.namespace}.${verb.method}.`);
63
+ }
64
+ if (!object(verb.args)) return invalid('Verb args must be a JSON Schema.');
65
+ try { ajv.compile(verb.args); } catch { return invalid('Invalid verb argument JSON Schema.'); }
66
+ const key = `${verb.namespace}.${verb.method}`;
67
+ if (seen.has(key)) return invalid(`Duplicate verb ${key}.`);
68
+ seen.add(key);
69
+ }
70
+ return v as unknown as PluginManifest;
71
+ }
72
+ export function assertSupportedPlugin(manifest: PluginManifest): void {
73
+ if (manifest.triggers.length || manifest.gates.length || manifest.verbs.some(v => v.lowersTo !== 'effect')) {
74
+ throw new PluginError('plugin_unsupported', 'This slice supports effect verbs; trigger, gate, run, llm, agent and wait dispatch are follow-ups.');
75
+ }
76
+ }
package/src/preflight.ts CHANGED
@@ -1,6 +1,16 @@
1
- import type { FlowSpec, StepSpec, TriggerSpec } from './spec.js';
1
+ import { loadPlugins, type LoadedPlugin } from './plugin-loader.js';
2
+ import { PluginError } from './plugin-manifest.js';
3
+ import type { FlowSpec, StepSpec, TriggerSpec, McpServerConfig } from './spec.js';
4
+ import { McpError, openMcpSession, type McpDiagnostic } from './mcp-client.js';
5
+ import { BudgetSyntaxError } from './budget.js';
6
+ import { budgetDiagnostics } from './budget-preflight.js';
7
+ import type { TriggerSource } from '@relayflows/surface';
2
8
  import { acceptsAnyOutput, inspectStepGate, type StepGateInspection } from './gate-contract.js';
3
9
  import { compileSpec, CompileError } from './compile.js';
10
+ import { helperCall } from './yaml-helpers.js';
11
+ import { isNamedGate, NAMED_GATE_FAILURE_KINDS, type NamedGateFailureKind } from './named-gates.js';
12
+ import { compileScopes, type ScopeInput, type MountRegistry } from './scope-compiler.js';
13
+ import { readMountRegistry } from './mount-registry.js';
4
14
  import type {
5
15
  PreflightFailureKind,
6
16
  PreflightWarningKind,
@@ -48,6 +58,8 @@ type CliProbeOutcome =
48
58
  * emits `probe_failed` (or `command_unprovable` for a deterministic command).
49
59
  */
50
60
  export interface PreflightProbes {
61
+ /** Whether the provider's relayfile mount is available to the helper worker. */
62
+ helper?(provider: string): boolean;
51
63
  /**
52
64
  * Resolve relative paths against the file implied by `source`, then probe
53
65
  * `auth status`. When the step declared a `model`, the probe runs with that
@@ -60,6 +72,10 @@ export interface PreflightProbes {
60
72
  }
61
73
 
62
74
  export interface PreflightOptions {
75
+ pluginSearchStart?: string;
76
+ /** Validated tools.mcp header and nearest flows.json connections. */
77
+ mcpServers?: readonly string[];
78
+ mcp?: Readonly<Record<string, McpServerConfig>>;
63
79
  projectCli?: string;
64
80
  projectConfigPath?: string;
65
81
  projectSearchStart?: string;
@@ -70,6 +86,8 @@ export interface PreflightOptions {
70
86
  }
71
87
 
72
88
  export interface PreflightRefusal {
89
+ server?: string;
90
+ cause?: McpDiagnostic;
73
91
  severity: 'refusal';
74
92
  kind: PreflightFailureKind;
75
93
  message: string;
@@ -94,13 +112,65 @@ export interface PreflightWarning {
94
112
  export type PreflightDiagnostic = PreflightRefusal | PreflightWarning;
95
113
 
96
114
  export interface PreflightResult {
115
+ plugins?: readonly LoadedPlugin[];
116
+ mcpTools?: Readonly<Record<string, readonly string[]>>;
97
117
  ok: boolean;
98
118
  gates: StepGateInspection[];
99
119
  resolutions: CliResolution[];
100
120
  diagnostics: PreflightDiagnostic[];
101
121
  }
102
122
 
103
- export function preflight(flow: FlowSpec, options: PreflightOptions): PreflightResult {
123
+ /** Pure declared-surface check, before opening a receiver or journal. */
124
+ export function preflightWebhookTriggers(
125
+ triggers: readonly TriggerSource[],
126
+ executors: readonly string[],
127
+ ): PreflightRefusal[] {
128
+ return [...new Set(triggers.map(trigger => trigger.name))]
129
+ .filter(name => !executors.includes(name))
130
+ .map(name => ({
131
+ severity: 'refusal',
132
+ kind: 'no_executor',
133
+ executor: name,
134
+ message: `webhook trigger "${name}" is not registered in flows.json`,
135
+ }));
136
+ }
137
+
138
+ // Preserve the synchronous declarative API; an authored MCP declaration opts
139
+ // into asynchronous connection probes after the same pure refusal pass.
140
+ export function preflight(flow: FlowSpec, options: PreflightOptions & { mcpServers: readonly string[] }): Promise<PreflightResult>;
141
+ export function preflight(flow: FlowSpec, options: PreflightOptions & { mcpServers?: undefined }): PreflightResult;
142
+ export function preflight(flow: FlowSpec, options: PreflightOptions): PreflightResult | Promise<PreflightResult>;
143
+ export function preflight(flow: unknown, options: PreflightOptions): PreflightResult | Promise<PreflightResult> {
144
+ const result = preflightSync(flow, options);
145
+ if (options.mcpServers === undefined) return result;
146
+ return probeMcp(result, options).then(async checked => {
147
+ if (!checked.ok || options.pluginSearchStart === undefined) return checked;
148
+ try { return { ...checked, plugins: await loadPlugins(options.pluginSearchStart) }; }
149
+ catch (error) {
150
+ if (!(error instanceof PluginError)) throw error;
151
+ return { ...checked, ok: false, diagnostics: [...checked.diagnostics, { severity: 'refusal' as const, kind: error.code, message: error.message }] };
152
+ }
153
+ });
154
+ }
155
+
156
+ async function probeMcp(result: PreflightResult, options: PreflightOptions): Promise<PreflightResult> {
157
+ if (!result.ok) return result;
158
+ const inventory: Record<string, readonly string[]> = Object.create(null);
159
+ for (const server of new Set(options.mcpServers)) {
160
+ try {
161
+ const session = await openMcpSession(options.mcp![server]!, 10_000);
162
+ try { inventory[server] = Object.freeze(await session.listTools()); }
163
+ finally { await session.close(); }
164
+ } catch (error) {
165
+ const cause = error instanceof McpError ? error.code : 'handshake_rejected';
166
+ result.diagnostics.push({ severity: 'refusal', kind: 'mcp_unreachable', server, cause,
167
+ message: `MCP server "${server}" is unreachable: ${cause}.` });
168
+ }
169
+ }
170
+ return { ...result, ok: !result.diagnostics.some(d => d.severity === 'refusal'), mcpTools: Object.freeze(inventory) };
171
+ }
172
+
173
+ function preflightSync(flow: unknown, options: PreflightOptions): PreflightResult {
104
174
  // Compile before touching any environment fact. `compileSpec` snapshots raw
105
175
  // input into inert data, validates it against the closed authoring schema,
106
176
  // and lowers `output` sugar into its json_schema gate — so the gate plan
@@ -108,20 +178,30 @@ export function preflight(flow: FlowSpec, options: PreflightOptions): PreflightR
108
178
  // inspection ever reads a live accessor. The failure is a named refusal
109
179
  // rather than a thrown error (RFC covenant 2), which is the contract main
110
180
  // settled for this boundary.
111
- let compiled: FlowSpec;
181
+ let compiled: import('./compile.js').CompiledFlowSpec;
112
182
  try {
113
183
  compiled = compileSpec(flow);
114
184
  } catch (error) {
115
185
  const errors = error instanceof CompileError
116
186
  ? error.errors
117
187
  : [error instanceof Error ? error.message : 'spec: expected JSON-compatible data'];
188
+ // BudgetSyntaxError now flows through CompileError with kind on it; the
189
+ // legacy raw-throw instanceof is preserved as a fallback so a caller that
190
+ // constructs preflight input through a different path still classifies.
191
+ const kind: PreflightDiagnostic['kind'] = error instanceof CompileError && error.kind === 'budget_syntax_invalid'
192
+ ? 'budget_syntax_invalid'
193
+ : error instanceof CompileError && NAMED_GATE_FAILURE_KINDS.includes(error.kind as NamedGateFailureKind)
194
+ ? error.kind as NamedGateFailureKind
195
+ : error instanceof BudgetSyntaxError
196
+ ? 'budget_syntax_invalid'
197
+ : 'invalid_spec';
118
198
  return {
119
199
  ok: false,
120
200
  gates: [],
121
201
  resolutions: [],
122
202
  diagnostics: [{
123
203
  severity: 'refusal',
124
- kind: 'invalid_spec',
204
+ kind,
125
205
  message: `Relayflow spec is invalid: ${errors.join('; ')}`,
126
206
  errors,
127
207
  }],
@@ -133,11 +213,19 @@ export function preflight(flow: FlowSpec, options: PreflightOptions): PreflightR
133
213
  const cliProbeResults = new Map<string, CliProbeOutcome>();
134
214
 
135
215
  diagnostics.push(...unknownModelDiagnostics(compiled, options));
216
+ diagnostics.push(...scopeDiagnostics(compiled, options));
217
+ for (const server of new Set(options.mcpServers ?? [])) {
218
+ if (options.mcp !== undefined && Object.hasOwn(options.mcp, server)) continue;
219
+ diagnostics.push({ severity: 'refusal', kind: 'mcp_undeclared_server', server,
220
+ message: `MCP server "${server}" is not declared in the nearest flows.json mcp map.` });
221
+ }
222
+ diagnostics.push(...budgetDiagnostics(compiled));
136
223
  // Resolve the complete flow before touching any environment fact. A later
137
224
  // statically unresolved CLI makes the whole submission impossible, so no
138
225
  // earlier command, provider/model, or trigger probe may run first.
139
226
  for (const step of compiled.steps) {
140
227
  if (step.type === 'deterministic') continue;
228
+ if (step.type === 'agent' && helperCall(step) !== undefined) continue;
141
229
  const resolution = resolveCli(step, compiled, options.projectCli);
142
230
  if (resolution === undefined) {
143
231
  diagnostics.push({
@@ -156,9 +244,26 @@ export function preflight(flow: FlowSpec, options: PreflightOptions): PreflightR
156
244
  }
157
245
 
158
246
  for (const step of compiled.steps) {
247
+ probeNamedGate(step, options.probes, diagnostics);
159
248
  warnOnVacuousGate(step, diagnostics);
160
249
  warnOnUnprovableEffects(step, options.probes, diagnostics);
161
250
  if (step.type === 'deterministic') continue;
251
+ const helper = step.type === 'agent' ? helperCall(step) : undefined;
252
+ if (helper !== undefined) {
253
+ try {
254
+ if (options.probes.helper === undefined) {
255
+ diagnostics.push({ severity: 'warning', kind: 'unprovable_effects', stepId: step.id,
256
+ message: `${helper.provider} helper requires a relayfile mount and an SDK agent worker.` });
257
+ } else if (!options.probes.helper(helper.provider)) {
258
+ diagnostics.push({ severity: 'refusal', kind: 'helper_mount_required', stepId: step.id,
259
+ message: `${helper.provider} helper requires a relayfile mount.` });
260
+ }
261
+ } catch {
262
+ diagnostics.push({ severity: 'refusal', kind: 'probe_failed', stepId: step.id,
263
+ message: `${helper.provider} helper mount could not be checked.` });
264
+ }
265
+ continue;
266
+ }
162
267
  const resolution = resolutionByStep.get(step.id)!;
163
268
  probeResolvedCli(resolution, options.probes, cliProbeResults, diagnostics);
164
269
  }
@@ -175,18 +280,55 @@ export function preflight(flow: FlowSpec, options: PreflightOptions): PreflightR
175
280
  };
176
281
  }
177
282
 
283
+ /**
284
+ * Compile author-declared `workspace:` / `tools.fs:` grants against the nearest
285
+ * relayfile mount manifest. The compiler is pure; only mount discovery reads a
286
+ * fact about the filesystem. Missing manifest means no known mounts — a grant
287
+ * still parses but refuses as `mount_unknown` in that case.
288
+ */
289
+ function scopeDiagnostics(flow: FlowSpec, options: PreflightOptions): PreflightRefusal[] {
290
+ const workspace = flow.workspace;
291
+ const toolsFs = flow.tools?.fs;
292
+ if (workspace === undefined && toolsFs === undefined) return [];
293
+ const input: ScopeInput = {
294
+ ...(workspace === undefined ? {} : { workspace }),
295
+ ...(toolsFs === undefined ? {} : { tools: { fs: toolsFs } }),
296
+ };
297
+ let mounts: MountRegistry | undefined;
298
+ if (options.projectSearchStart !== undefined) {
299
+ try { mounts = readMountRegistry(options.projectSearchStart); }
300
+ catch { mounts = {}; /* fail closed — unreadable manifest treats every mount as unknown */ }
301
+ }
302
+ return compileScopes(input, mounts).diagnostics.map(refusal => ({
303
+ severity: 'refusal',
304
+ kind: refusal.kind,
305
+ message: refusal.message,
306
+ ...(refusal.stepId === undefined ? {} : { stepId: refusal.stepId }),
307
+ }));
308
+ }
309
+
178
310
  /** Pure authoring validation: no executable, command, trigger, or daemon probe. */
179
311
  function unknownModelDiagnostics(
180
312
  flow: FlowSpec,
181
313
  options: PreflightOptions,
182
314
  ): PreflightRefusal[] {
183
315
  const diagnostics: PreflightRefusal[] = [];
316
+ // model_unknown is a governance check: it exists to enforce a project's
317
+ // registry-declared allowlist. When no flows.json is found, `check.ts` sends
318
+ // `models: []` with `modelRegistryPath: undefined` — an empty list not
319
+ // because the project forbids everything, but because no policy exists.
320
+ // Refusing an inline `agents: { drafter: { cli, model } }` declaration in
321
+ // that state forces every self-contained example flow to ship a second file.
322
+ // A real allowlist (even an empty one from a found flows.json) still
323
+ // enforces; that state is signalled by modelRegistryPath.
324
+ const enforceRegistry = options.modelRegistryPath !== undefined;
184
325
 
185
326
  // Named declarations remain in the normalized authoring object until this
186
327
  // boundary so even unused or step-shadowed models are checked. toKernelSpec
187
328
  // erases the map and selector only after this pass has had a chance to fail.
188
329
  for (const [agent, declaration] of Object.entries(flow.agents ?? {})) {
189
330
  if (isKnownModel(declaration.model, options.models)) continue;
331
+ if (!enforceRegistry) continue;
190
332
  diagnostics.push({
191
333
  severity: 'refusal',
192
334
  kind: 'model_unknown',
@@ -480,3 +622,35 @@ function firstCommandWord(command: string): string | undefined {
480
622
  const match = rest.match(/^(?:"([^"]+)"|'([^']+)'|([^\s]+))/);
481
623
  return match?.[1] ?? match?.[2] ?? match?.[3];
482
624
  }
625
+
626
+ function probeNamedGate(step: StepSpec, probes: PreflightProbes, diagnostics: PreflightDiagnostic[]): void {
627
+ const gate = step.verification;
628
+ if (!isNamedGate(gate)) return;
629
+ const commands = ['node', ...(gate.type === 'word_count_bounds' ? ['wc'] : [])];
630
+ if (gate.type === 'subprocess_gate') commands.push(firstCommandWord(gate.command) ?? '');
631
+ for (const command of commands) {
632
+ let exists = false;
633
+ try { exists = command !== '' && probes.command(command); } catch { /* Fail closed on an unprovable gate. */ }
634
+ if (exists) continue;
635
+ diagnostics.push({ severity: 'refusal', kind: 'gate_command_missing', stepId: step.id,
636
+ message: `Step "${step.id}" ${gate.type} command "${command}" does not resolve as an executable.` });
637
+ }
638
+ }
639
+
640
+ export { preflightHelpers } from './helper-preflight.js';
641
+
642
+ /** Authored memory probes run before invoking the body or contacting the journal. */
643
+ export async function preflightMemory(
644
+ probe: () => Promise<void>,
645
+ ): Promise<PreflightRefusal | undefined> {
646
+ try {
647
+ await probe();
648
+ return undefined;
649
+ } catch {
650
+ return {
651
+ severity: 'refusal',
652
+ kind: 'memory_unreachable',
653
+ message: 'Script memory requires the ai-hist Node SDK and a readable SQLite database at AI_HIST_DB (or defaultDbPath()). Run ai-hist sync first.',
654
+ };
655
+ }
656
+ }
@@ -0,0 +1,51 @@
1
+ import type { CompletionReason } from './protocol.js';
2
+ import type { StepType } from './spec.js';
3
+
4
+ export interface ProgressEvent {
5
+ type: 'step.started' | 'step.running' | 'step.completed' | 'step.failed';
6
+ stepId: string;
7
+ stepType: StepType;
8
+ elapsedMs: number;
9
+ completionReason?: CompletionReason;
10
+ }
11
+
12
+ /** Pure terminal rendering: caller owns the event source, clock, and output. */
13
+ export function renderProgress(events: Iterable<ProgressEvent>): string[] {
14
+ return Array.from(events, event => {
15
+ const icon = { 'step.started': '○', 'step.running': '↻', 'step.completed': '✓', 'step.failed': '✗' }[event.type];
16
+ const state = event.type.slice('step.'.length);
17
+ const agent = event.stepType === 'agent' ? ` [agent: ${state === 'started' ? 'preparing' : state}]` : '';
18
+ const reason = event.completionReason ? ` completionReason: ${event.completionReason}` : '';
19
+ // Agent-authored names cannot inject terminal control sequences.
20
+ const name = event.stepId.replace(/[\x00-\x1f\x7f-\x9f]/g, '?');
21
+ return `${icon} ${name} (${event.stepType})${agent} ${(Math.max(0, event.elapsedMs) / 1000).toFixed(2)}s${reason}`;
22
+ });
23
+ }
24
+
25
+ /** Observe the existing executor; success is emitted only after its journal read. */
26
+ export async function observeStep<T>(
27
+ stepId: string,
28
+ stepType: StepType,
29
+ execute: () => Promise<T>,
30
+ emit?: (event: ProgressEvent) => void,
31
+ ): Promise<T> {
32
+ const publish = (event: ProgressEvent): void => {
33
+ try { emit?.(event); } catch {
34
+ // A projection failure must not turn a journaled success into a retry,
35
+ // or replace the executor's original failure. Surface it separately.
36
+ process.emitWarning(`Progress observer failed for ${event.type}.`, {
37
+ code: 'FLOWS_PROGRESS_OBSERVER_ERROR',
38
+ });
39
+ }
40
+ };
41
+ const started = performance.now();
42
+ publish({ type: 'step.started', stepId, stepType, elapsedMs: 0 });
43
+ try {
44
+ const result = await execute();
45
+ publish({ type: 'step.completed', stepId, stepType, elapsedMs: performance.now() - started, completionReason: 'success' });
46
+ return result;
47
+ } catch (error) {
48
+ publish({ type: 'step.failed', stepId, stepType, elapsedMs: performance.now() - started });
49
+ throw error;
50
+ }
51
+ }
package/src/protocol.ts CHANGED
@@ -69,7 +69,15 @@ export interface HelloResult {
69
69
  server: string;
70
70
  }
71
71
 
72
+ export interface StepSpend {
73
+ tokens_input: number;
74
+ tokens_output: number;
75
+ dollars: number;
76
+ wallclock_ms: number;
77
+ }
78
+
72
79
  export interface RunStartParams {
80
+ reuse_from_run_id?: string;
73
81
  /**
74
82
  * The kernel spec dialect — the ONE boundary shape `RunSpec::parse`
75
83
  * accepts (snake_case, flat v0 verification, defaults materialized).
@@ -90,6 +98,7 @@ export interface RunOutcome {
90
98
  export type RunStartResult = RunOutcome;
91
99
 
92
100
  export interface RunResumeParams {
101
+ allow_human_influenced?: boolean;
93
102
  run_id: string;
94
103
  }
95
104
  export type RunResumeResult = RunOutcome;
@@ -179,6 +188,8 @@ export interface RoutingDecision {
179
188
  }
180
189
 
181
190
  export interface StepDispatchEvent {
191
+ /** Selected successful outputs, resolved by the kernel from the journal. */
192
+ input?: Record<string, unknown>;
182
193
  /** Durable choice; optional only for older kernel protocol compatibility. */
183
194
  routing?: RoutingDecision;
184
195
  /** Already journaled and charged; completion usage excludes this cost. */
@@ -287,6 +298,7 @@ export interface EffectConfirmResult {
287
298
  }
288
299
 
289
300
  export interface StepCompleteParams {
301
+ human_intervention?: boolean;
290
302
  run_id: string;
291
303
  step_id: string;
292
304
  attempt: number;
@@ -0,0 +1,92 @@
1
+ import { createServer, type Socket } from 'node:net';
2
+ import { chmod, mkdir } from 'node:fs/promises';
3
+ import { dirname, join, resolve } from 'node:path';
4
+
5
+ export interface SidechannelContext {
6
+ dataDir: string;
7
+ runId: string;
8
+ stepId: string;
9
+ onReady?: (path: string) => void;
10
+ onDrive: () => void;
11
+ }
12
+
13
+ export function ptySocketPath(context: Pick<SidechannelContext, 'dataDir' | 'runId' | 'stepId'>): string {
14
+ for (const id of [context.runId, context.stepId]) {
15
+ if (!id || id === '.' || id === '..' || /[/\\\0]/.test(id)) throw new Error('Invalid sidechannel path component');
16
+ }
17
+ return join(resolve(context.dataDir), 'runs', context.runId, 'steps', context.stepId, 'pty.sock');
18
+ }
19
+
20
+ /** Best-effort live bytes. Slow peers are dropped; they never pause execution. */
21
+ export async function openSidechannel(
22
+ context: SidechannelContext,
23
+ input: (bytes: Buffer) => boolean | Promise<boolean>,
24
+ canDrive: () => boolean = () => true,
25
+ ) {
26
+ const peers = new Map<Socket, boolean>();
27
+ let closed = false;
28
+ const server = createServer(socket => {
29
+ if (peers.size >= 16) { socket.destroy(); return; }
30
+ peers.set(socket, false);
31
+ let hello = Buffer.alloc(0);
32
+ let mode: string | undefined;
33
+ socket.setTimeout(2_000, () => socket.destroy());
34
+ socket.on('error', () => socket.destroy());
35
+ socket.on('close', () => peers.delete(socket));
36
+ socket.on('data', (bytes: Buffer) => {
37
+ if (mode === undefined) {
38
+ hello = Buffer.concat([hello, bytes]);
39
+ const end = hello.indexOf(10);
40
+ if (end < 0) { if (hello.length > 32) socket.destroy(); return; }
41
+ const line = hello.subarray(0, end).toString('utf8');
42
+ if (!['HELLO view', 'HELLO drive', 'HELLO passthrough'].includes(line)) { socket.destroy(); return; }
43
+ mode = line.slice(6);
44
+ if (mode === 'drive' && !canDrive()) { socket.destroy(); return; }
45
+ socket.setTimeout(0);
46
+ peers.set(socket, true);
47
+ // Passthrough is a passive raw-byte view in this initial slice.
48
+ if (mode === 'drive') context.onDrive();
49
+ bytes = hello.subarray(end + 1);
50
+ hello = Buffer.alloc(0);
51
+ }
52
+ if (mode === 'drive' && bytes.length > 0) {
53
+ socket.pause();
54
+ void Promise.resolve().then(() => input(bytes)).then(accepted => {
55
+ if (!accepted) socket.destroy();
56
+ else if (!socket.destroyed) socket.resume();
57
+ }, () => socket.destroy());
58
+ }
59
+ });
60
+ });
61
+ server.on('error', () => { for (const peer of peers.keys()) peer.destroy(); });
62
+ try {
63
+ const path = ptySocketPath(context);
64
+ // Restrict traversal as well as socket access to the worker's OS user.
65
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
66
+ await chmod(dirname(path), 0o700);
67
+ await new Promise<void>((resolve, reject) => {
68
+ server.once('error', reject);
69
+ server.listen(path, () => { server.off('error', reject); resolve(); });
70
+ });
71
+ await chmod(path, 0o600);
72
+ context.onReady?.(path);
73
+ } catch {
74
+ // Never unlink a pre-existing socket: it may belong to a live attempt.
75
+ if (server.listening) server.close();
76
+ return undefined;
77
+ }
78
+ return {
79
+ publish(bytes: Buffer) {
80
+ if (closed) return;
81
+ for (const [peer, ready] of peers) {
82
+ if (ready && !peer.write(bytes)) peer.destroy();
83
+ }
84
+ },
85
+ close() {
86
+ if (closed) return;
87
+ closed = true;
88
+ for (const peer of peers.keys()) peer.destroy();
89
+ server.close();
90
+ },
91
+ };
92
+ }
@@ -0,0 +1,105 @@
1
+ import { snapshotJsonValue } from './json-value.js';
2
+
3
+ export const SCOPE_MODES = ['readonly', 'readwrite', 'append'] as const;
4
+ export type ScopeMode = (typeof SCOPE_MODES)[number];
5
+ export type ScopeGrants = string | readonly string[];
6
+
7
+ /** Inert path-prefix permission; paths are relative to the named mount. */
8
+ export interface ScopeDescriptor {
9
+ mount: string;
10
+ path: string;
11
+ mode: ScopeMode;
12
+ }
13
+
14
+ /** Facts supplied by relayfile's mount manifest, never an author-side ACL. */
15
+ export type MountRegistry = Readonly<Record<string, readonly {
16
+ path: string;
17
+ modes: readonly ScopeMode[];
18
+ }[]>>;
19
+
20
+ export interface ScopeDeclaration {
21
+ source: string;
22
+ stepId?: string;
23
+ grant: string;
24
+ scope: ScopeDescriptor;
25
+ }
26
+
27
+ export interface ScopeRefusal {
28
+ severity: 'refusal';
29
+ kind: 'scope_syntax_invalid' | 'mount_unknown' | 'scope_ungrantable';
30
+ message: string;
31
+ source: string;
32
+ stepId?: string;
33
+ grant?: string;
34
+ scope?: ScopeDescriptor;
35
+ }
36
+
37
+ export interface ScopeCompilation {
38
+ scopes: ScopeDeclaration[];
39
+ diagnostics: ScopeRefusal[];
40
+ }
41
+
42
+ export interface ScopeInput {
43
+ workspace?: ScopeGrants;
44
+ tools?: { fs?: ScopeGrants };
45
+ steps?: readonly { id: string; workspace?: ScopeGrants; tools?: { fs?: ScopeGrants } }[];
46
+ }
47
+
48
+ /** Reject ambiguous or traversing paths instead of normalizing permissions. */
49
+ export function isScopePath(path: unknown): path is string {
50
+ return typeof path === 'string' && path.length > 0
51
+ && path.split('/').every(part => /^[A-Za-z0-9._-]+$/.test(part)
52
+ && part !== '.' && part !== '..');
53
+ }
54
+
55
+ export function parseScopeGrant(grant: string): ScopeDescriptor | undefined {
56
+ const match = /^([A-Za-z0-9_-]+)\/(.+):\s*(readonly|readwrite|append)$/.exec(grant);
57
+ if (match === null || match[0] !== grant || !isScopePath(match[2])) return undefined;
58
+ return { mount: match[1]!, path: match[2]!, mode: match[3] as ScopeMode };
59
+ }
60
+
61
+ /** One pure pass collects every declaration error before any live probe. */
62
+ export function compileScopes(input: ScopeInput, mounts?: MountRegistry): ScopeCompilation {
63
+ const result: ScopeCompilation = { scopes: [], diagnostics: [] };
64
+ // Public callers may be JS: never run a getter while collecting permissions.
65
+ const flow = snapshotJsonValue(input, 'scope declarations') as unknown as ScopeInput;
66
+ function collect(value: unknown, source: string, stepId?: string): void {
67
+ if (value === undefined) return;
68
+ const location = { source, ...(stepId === undefined ? {} : { stepId }) };
69
+ const grants = Array.isArray(value) ? value : [value];
70
+ for (const grant of grants) {
71
+ const scope = typeof grant === 'string' ? parseScopeGrant(grant) : undefined;
72
+ if (scope === undefined) {
73
+ result.diagnostics.push({
74
+ ...location, severity: 'refusal', kind: 'scope_syntax_invalid',
75
+ ...(typeof grant === 'string' ? { grant } : {}),
76
+ message: `${source}: expected "<mount>/<path>: readonly|readwrite|append" with a canonical path prefix.`,
77
+ });
78
+ continue;
79
+ }
80
+ const declaration = { ...location, grant: grant as string, scope };
81
+ result.scopes.push(declaration);
82
+ if (mounts === undefined) continue; // Compiler does not invent environment facts.
83
+ if (!Object.hasOwn(mounts, scope.mount)) {
84
+ result.diagnostics.push({
85
+ ...declaration, severity: 'refusal', kind: 'mount_unknown',
86
+ message: `${source}: mount "${scope.mount}" is not present in the relayfile mount manifest.`,
87
+ });
88
+ } else if (!mounts[scope.mount]!.some(available =>
89
+ available.modes.includes(scope.mode)
90
+ && (scope.path === available.path || scope.path.startsWith(`${available.path}/`)))) {
91
+ result.diagnostics.push({
92
+ ...declaration, severity: 'refusal', kind: 'scope_ungrantable',
93
+ message: `${source}: relayfile cannot grant "${grant}" from the mount's available path permissions.`,
94
+ });
95
+ }
96
+ }
97
+ }
98
+ collect(flow.workspace, 'workspace');
99
+ collect(flow.tools?.fs, 'tools.fs');
100
+ for (const step of flow.steps ?? []) {
101
+ collect(step.workspace, `step "${step.id}".workspace`, step.id);
102
+ collect(step.tools?.fs, `step "${step.id}".tools.fs`, step.id);
103
+ }
104
+ return result;
105
+ }
@@ -0,0 +1,25 @@
1
+ import { statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { helperProviders } from '@relayflows/surface/runtime';
4
+ import { preflightHelpers } from './preflight.js';
5
+
6
+ export function providerMount(provider: string, env: NodeJS.ProcessEnv = process.env): string | undefined {
7
+ const root = [env.RELAYFILE_MOUNT_PATH, env.WORKSPACE_ROOT, env.WORKFORCE_SANDBOX_ROOT,
8
+ env.RELAYFILE_MOUNT_ROOT, env.RELAYFILE_ROOT].find(value => value?.trim());
9
+ if (!root?.trim()) return undefined;
10
+ try { return statSync(join(root, provider)).isDirectory() ? root : undefined; }
11
+ catch { return undefined; }
12
+ }
13
+
14
+ export const slackMount = (env: NodeJS.ProcessEnv = process.env) => providerMount('slack', env);
15
+
16
+ export function checkProviderHelpers(definition: Parameters<typeof preflightHelpers>[0]) {
17
+ return preflightHelpers(definition, {
18
+ providers: Object.fromEntries(helperProviders.map(p => [p.provider, {
19
+ mount: providerMount(p.provider) !== undefined,
20
+ mock: process.env[p.mockEnv] === '1',
21
+ token: p.provider === 'slack' ? process.env.SLACK_BOT_TOKEN : undefined,
22
+ }])),
23
+ });
24
+ }
25
+ export const checkSlackHelpers = checkProviderHelpers;