@relayflows/sdk 2.0.8 → 2.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/dist/adapters/base.d.ts +46 -0
  2. package/dist/adapters/base.d.ts.map +1 -0
  3. package/dist/adapters/base.js +15 -0
  4. package/dist/adapters/base.js.map +1 -0
  5. package/dist/adapters/claude.d.ts +5 -0
  6. package/dist/adapters/claude.d.ts.map +1 -0
  7. package/dist/adapters/claude.js +44 -0
  8. package/dist/adapters/claude.js.map +1 -0
  9. package/dist/adapters/codex.d.ts +5 -0
  10. package/dist/adapters/codex.d.ts.map +1 -0
  11. package/dist/adapters/codex.js +45 -0
  12. package/dist/adapters/codex.js.map +1 -0
  13. package/dist/adapters/index.d.ts +14 -0
  14. package/dist/adapters/index.d.ts.map +1 -0
  15. package/dist/adapters/index.js +29 -0
  16. package/dist/adapters/index.js.map +1 -0
  17. package/dist/adapters/wrapper.d.ts +12 -0
  18. package/dist/adapters/wrapper.d.ts.map +1 -0
  19. package/dist/adapters/wrapper.js +31 -0
  20. package/dist/adapters/wrapper.js.map +1 -0
  21. package/dist/agent-relay-transport.d.ts +84 -0
  22. package/dist/agent-relay-transport.d.ts.map +1 -0
  23. package/dist/agent-relay-transport.js +134 -0
  24. package/dist/agent-relay-transport.js.map +1 -0
  25. package/dist/authored-budget.d.ts +13 -0
  26. package/dist/authored-budget.d.ts.map +1 -0
  27. package/dist/authored-budget.js +78 -0
  28. package/dist/authored-budget.js.map +1 -0
  29. package/dist/authored-flow-error.d.ts +1 -1
  30. package/dist/authored-flow-error.d.ts.map +1 -1
  31. package/dist/authored-flow-error.js.map +1 -1
  32. package/dist/authored-flow-executor.d.ts +13 -7
  33. package/dist/authored-flow-executor.d.ts.map +1 -1
  34. package/dist/authored-flow-executor.js +115 -194
  35. package/dist/authored-flow-executor.js.map +1 -1
  36. package/dist/authored-flow-loader.d.ts +10 -1
  37. package/dist/authored-flow-loader.d.ts.map +1 -1
  38. package/dist/authored-flow-loader.js +56 -3
  39. package/dist/authored-flow-loader.js.map +1 -1
  40. package/dist/authored-flow-operation.d.ts +9 -1
  41. package/dist/authored-flow-operation.d.ts.map +1 -1
  42. package/dist/authored-flow-operation.js +35 -4
  43. package/dist/authored-flow-operation.js.map +1 -1
  44. package/dist/authored-helper-effect.d.ts +12 -0
  45. package/dist/authored-helper-effect.d.ts.map +1 -0
  46. package/dist/authored-helper-effect.js +144 -0
  47. package/dist/authored-helper-effect.js.map +1 -0
  48. package/dist/authored-mcp.d.ts +17 -0
  49. package/dist/authored-mcp.d.ts.map +1 -0
  50. package/dist/authored-mcp.js +171 -0
  51. package/dist/authored-mcp.js.map +1 -0
  52. package/dist/authored-memory.d.ts +7 -0
  53. package/dist/authored-memory.d.ts.map +1 -0
  54. package/dist/authored-memory.js +60 -0
  55. package/dist/authored-memory.js.map +1 -0
  56. package/dist/authored-plugin-effect.d.ts +16 -0
  57. package/dist/authored-plugin-effect.d.ts.map +1 -0
  58. package/dist/authored-plugin-effect.js +163 -0
  59. package/dist/authored-plugin-effect.js.map +1 -0
  60. package/dist/authored-step-output.d.ts +23 -0
  61. package/dist/authored-step-output.d.ts.map +1 -0
  62. package/dist/authored-step-output.js +67 -0
  63. package/dist/authored-step-output.js.map +1 -0
  64. package/dist/authored-worker-step.d.ts +15 -0
  65. package/dist/authored-worker-step.d.ts.map +1 -0
  66. package/dist/authored-worker-step.js +136 -0
  67. package/dist/authored-worker-step.js.map +1 -0
  68. package/dist/budget-preflight.d.ts +5 -0
  69. package/dist/budget-preflight.d.ts.map +1 -0
  70. package/dist/budget-preflight.js +33 -0
  71. package/dist/budget-preflight.js.map +1 -0
  72. package/dist/budget.d.ts +14 -0
  73. package/dist/budget.d.ts.map +1 -0
  74. package/dist/budget.js +52 -0
  75. package/dist/budget.js.map +1 -0
  76. package/dist/bundle-transport.d.ts +18 -0
  77. package/dist/bundle-transport.d.ts.map +1 -0
  78. package/dist/bundle-transport.js +111 -0
  79. package/dist/bundle-transport.js.map +1 -0
  80. package/dist/bundle-typescript.d.ts +8 -0
  81. package/dist/bundle-typescript.d.ts.map +1 -0
  82. package/dist/bundle-typescript.js +108 -0
  83. package/dist/bundle-typescript.js.map +1 -0
  84. package/dist/bundle.d.ts +25 -0
  85. package/dist/bundle.d.ts.map +1 -0
  86. package/dist/bundle.js +199 -0
  87. package/dist/bundle.js.map +1 -0
  88. package/dist/child-stop.d.ts +59 -0
  89. package/dist/child-stop.d.ts.map +1 -0
  90. package/dist/child-stop.js +94 -0
  91. package/dist/child-stop.js.map +1 -0
  92. package/dist/cli/add.d.ts +6 -0
  93. package/dist/cli/add.d.ts.map +1 -0
  94. package/dist/cli/add.js +47 -0
  95. package/dist/cli/add.js.map +1 -0
  96. package/dist/cli/build.d.ts +13 -0
  97. package/dist/cli/build.d.ts.map +1 -0
  98. package/dist/cli/build.js +224 -0
  99. package/dist/cli/build.js.map +1 -0
  100. package/dist/cli/bundle-preflight.d.ts +5 -0
  101. package/dist/cli/bundle-preflight.d.ts.map +1 -0
  102. package/dist/cli/bundle-preflight.js +28 -0
  103. package/dist/cli/bundle-preflight.js.map +1 -0
  104. package/dist/cli/check-helper-body.d.ts +4 -0
  105. package/dist/cli/check-helper-body.d.ts.map +1 -0
  106. package/dist/cli/check-helper-body.js +15 -0
  107. package/dist/cli/check-helper-body.js.map +1 -0
  108. package/dist/cli/check-triggers.d.ts +14 -0
  109. package/dist/cli/check-triggers.d.ts.map +1 -0
  110. package/dist/cli/check-triggers.js +37 -0
  111. package/dist/cli/check-triggers.js.map +1 -0
  112. package/dist/cli/check-typescript.d.ts +20 -0
  113. package/dist/cli/check-typescript.d.ts.map +1 -0
  114. package/dist/cli/check-typescript.js +54 -0
  115. package/dist/cli/check-typescript.js.map +1 -0
  116. package/dist/cli/check.d.ts +36 -3
  117. package/dist/cli/check.d.ts.map +1 -1
  118. package/dist/cli/check.js +122 -11
  119. package/dist/cli/check.js.map +1 -1
  120. package/dist/cli/cloud-run.d.ts +8 -0
  121. package/dist/cli/cloud-run.d.ts.map +1 -0
  122. package/dist/cli/cloud-run.js +50 -0
  123. package/dist/cli/cloud-run.js.map +1 -0
  124. package/dist/cli/deploy.d.ts +9 -0
  125. package/dist/cli/deploy.d.ts.map +1 -0
  126. package/dist/cli/deploy.js +58 -0
  127. package/dist/cli/deploy.js.map +1 -0
  128. package/dist/cli/deterministic-failure.d.ts +5 -0
  129. package/dist/cli/deterministic-failure.d.ts.map +1 -0
  130. package/dist/cli/deterministic-failure.js +66 -0
  131. package/dist/cli/deterministic-failure.js.map +1 -0
  132. package/dist/cli/direct-run.d.ts.map +1 -1
  133. package/dist/cli/direct-run.js +83 -7
  134. package/dist/cli/direct-run.js.map +1 -1
  135. package/dist/cli/hn-monitor.d.ts.map +1 -1
  136. package/dist/cli/hn-monitor.js +2 -1
  137. package/dist/cli/hn-monitor.js.map +1 -1
  138. package/dist/cli/replay.d.ts +12 -0
  139. package/dist/cli/replay.d.ts.map +1 -0
  140. package/dist/cli/replay.js +82 -0
  141. package/dist/cli/replay.js.map +1 -0
  142. package/dist/cli/reuse.d.ts +8 -0
  143. package/dist/cli/reuse.d.ts.map +1 -0
  144. package/dist/cli/reuse.js +25 -0
  145. package/dist/cli/reuse.js.map +1 -0
  146. package/dist/cli/run-digest.d.ts +4 -0
  147. package/dist/cli/run-digest.d.ts.map +1 -0
  148. package/dist/cli/run-digest.js +24 -0
  149. package/dist/cli/run-digest.js.map +1 -0
  150. package/dist/cli/run.d.ts +16 -5
  151. package/dist/cli/run.d.ts.map +1 -1
  152. package/dist/cli/run.js +99 -17
  153. package/dist/cli/run.js.map +1 -1
  154. package/dist/cli/serve-webhook.d.ts +40 -0
  155. package/dist/cli/serve-webhook.d.ts.map +1 -0
  156. package/dist/cli/serve-webhook.js +233 -0
  157. package/dist/cli/serve-webhook.js.map +1 -0
  158. package/dist/cli/tick-runner.d.ts.map +1 -1
  159. package/dist/cli/tick-runner.js +2 -1
  160. package/dist/cli/tick-runner.js.map +1 -1
  161. package/dist/cli-adapter.d.ts +8 -14
  162. package/dist/cli-adapter.d.ts.map +1 -1
  163. package/dist/cli-adapter.js +19 -64
  164. package/dist/cli-adapter.js.map +1 -1
  165. package/dist/cli-watch.d.ts +13 -0
  166. package/dist/cli-watch.d.ts.map +1 -0
  167. package/dist/cli-watch.js +185 -0
  168. package/dist/cli-watch.js.map +1 -0
  169. package/dist/cli.d.ts +4 -0
  170. package/dist/cli.d.ts.map +1 -1
  171. package/dist/cli.js +387 -14
  172. package/dist/cli.js.map +1 -1
  173. package/dist/cloud-http.d.ts +22 -0
  174. package/dist/cloud-http.d.ts.map +1 -0
  175. package/dist/cloud-http.js +84 -0
  176. package/dist/cloud-http.js.map +1 -0
  177. package/dist/cloud-run.d.ts +38 -0
  178. package/dist/cloud-run.d.ts.map +1 -0
  179. package/dist/cloud-run.js +112 -0
  180. package/dist/cloud-run.js.map +1 -0
  181. package/dist/compile.d.ts +21 -3
  182. package/dist/compile.d.ts.map +1 -1
  183. package/dist/compile.js +97 -23
  184. package/dist/compile.js.map +1 -1
  185. package/dist/create-flow.d.ts +17 -0
  186. package/dist/create-flow.d.ts.map +1 -0
  187. package/dist/create-flow.js +72 -0
  188. package/dist/create-flow.js.map +1 -0
  189. package/dist/daemon-connection.d.ts +14 -0
  190. package/dist/daemon-connection.d.ts.map +1 -1
  191. package/dist/daemon-connection.js +33 -1
  192. package/dist/daemon-connection.js.map +1 -1
  193. package/dist/demo-hn-monitor.js +2 -1
  194. package/dist/demo-hn-monitor.js.map +1 -1
  195. package/dist/effect-channel.d.ts +30 -0
  196. package/dist/effect-channel.d.ts.map +1 -0
  197. package/dist/effect-channel.js +71 -0
  198. package/dist/effect-channel.js.map +1 -0
  199. package/dist/failure-kinds.d.ts +14 -3
  200. package/dist/failure-kinds.d.ts.map +1 -1
  201. package/dist/failure-kinds.js +27 -0
  202. package/dist/failure-kinds.js.map +1 -1
  203. package/dist/gate-contract.d.ts.map +1 -1
  204. package/dist/gate-contract.js +9 -0
  205. package/dist/gate-contract.js.map +1 -1
  206. package/dist/helper-preflight.d.ts +18 -0
  207. package/dist/helper-preflight.d.ts.map +1 -0
  208. package/dist/helper-preflight.js +32 -0
  209. package/dist/helper-preflight.js.map +1 -0
  210. package/dist/helper-storage.d.ts +5 -0
  211. package/dist/helper-storage.d.ts.map +1 -0
  212. package/dist/helper-storage.js +31 -0
  213. package/dist/helper-storage.js.map +1 -0
  214. package/dist/helper-writeback.d.ts +12 -0
  215. package/dist/helper-writeback.d.ts.map +1 -0
  216. package/dist/helper-writeback.js +95 -0
  217. package/dist/helper-writeback.js.map +1 -0
  218. package/dist/index.d.ts +10 -2
  219. package/dist/index.d.ts.map +1 -1
  220. package/dist/index.js +7 -1
  221. package/dist/index.js.map +1 -1
  222. package/dist/input-binding.d.ts +4 -0
  223. package/dist/input-binding.d.ts.map +1 -0
  224. package/dist/input-binding.js +78 -0
  225. package/dist/input-binding.js.map +1 -0
  226. package/dist/journal-client.d.ts +12 -4
  227. package/dist/journal-client.d.ts.map +1 -1
  228. package/dist/journal-client.js +17 -6
  229. package/dist/journal-client.js.map +1 -1
  230. package/dist/journal-offset.d.ts +3 -0
  231. package/dist/journal-offset.d.ts.map +1 -0
  232. package/dist/journal-offset.js +69 -0
  233. package/dist/journal-offset.js.map +1 -0
  234. package/dist/journal-reader.d.ts +30 -0
  235. package/dist/journal-reader.d.ts.map +1 -0
  236. package/dist/journal-reader.js +156 -0
  237. package/dist/journal-reader.js.map +1 -0
  238. package/dist/json-schema.d.ts +2 -0
  239. package/dist/json-schema.d.ts.map +1 -1
  240. package/dist/json-schema.js +38 -22
  241. package/dist/json-schema.js.map +1 -1
  242. package/dist/llm-worker.d.ts +16 -0
  243. package/dist/llm-worker.d.ts.map +1 -0
  244. package/dist/llm-worker.js +79 -0
  245. package/dist/llm-worker.js.map +1 -0
  246. package/dist/local-agent.d.ts +8 -0
  247. package/dist/local-agent.d.ts.map +1 -0
  248. package/dist/local-agent.js +25 -0
  249. package/dist/local-agent.js.map +1 -0
  250. package/dist/mcp-client.d.ts +15 -0
  251. package/dist/mcp-client.d.ts.map +1 -0
  252. package/dist/mcp-client.js +104 -0
  253. package/dist/mcp-client.js.map +1 -0
  254. package/dist/mcp-config.d.ts +4 -0
  255. package/dist/mcp-config.d.ts.map +1 -0
  256. package/dist/mcp-config.js +61 -0
  257. package/dist/mcp-config.js.map +1 -0
  258. package/dist/mcp-stdio.d.ts +24 -0
  259. package/dist/mcp-stdio.d.ts.map +1 -0
  260. package/dist/mcp-stdio.js +89 -0
  261. package/dist/mcp-stdio.js.map +1 -0
  262. package/dist/model-pricing.d.ts +22 -0
  263. package/dist/model-pricing.d.ts.map +1 -0
  264. package/dist/model-pricing.js +30 -0
  265. package/dist/model-pricing.js.map +1 -0
  266. package/dist/mount-registry.d.ts +9 -0
  267. package/dist/mount-registry.d.ts.map +1 -0
  268. package/dist/mount-registry.js +47 -0
  269. package/dist/mount-registry.js.map +1 -0
  270. package/dist/named-gate-lowering.d.ts +9 -0
  271. package/dist/named-gate-lowering.d.ts.map +1 -0
  272. package/dist/named-gate-lowering.js +152 -0
  273. package/dist/named-gate-lowering.js.map +1 -0
  274. package/dist/named-gates.d.ts +10 -0
  275. package/dist/named-gates.d.ts.map +1 -0
  276. package/dist/named-gates.js +69 -0
  277. package/dist/named-gates.js.map +1 -0
  278. package/dist/observer-link.d.ts +129 -0
  279. package/dist/observer-link.d.ts.map +1 -0
  280. package/dist/observer-link.js +274 -0
  281. package/dist/observer-link.js.map +1 -0
  282. package/dist/plugin-loader.d.ts +13 -0
  283. package/dist/plugin-loader.d.ts.map +1 -0
  284. package/dist/plugin-loader.js +110 -0
  285. package/dist/plugin-loader.js.map +1 -0
  286. package/dist/plugin-manifest.d.ts +27 -0
  287. package/dist/plugin-manifest.d.ts.map +1 -0
  288. package/dist/plugin-manifest.js +89 -0
  289. package/dist/plugin-manifest.js.map +1 -0
  290. package/dist/preflight.d.ts +26 -2
  291. package/dist/preflight.d.ts.map +1 -1
  292. package/dist/preflight.js +175 -1
  293. package/dist/preflight.js.map +1 -1
  294. package/dist/progress.d.ts +14 -0
  295. package/dist/progress.d.ts.map +1 -0
  296. package/dist/progress.js +39 -0
  297. package/dist/progress.js.map +1 -0
  298. package/dist/protocol.d.ts +11 -0
  299. package/dist/protocol.d.ts.map +1 -1
  300. package/dist/pty-sidechannel.d.ts +14 -0
  301. package/dist/pty-sidechannel.d.ts.map +1 -0
  302. package/dist/pty-sidechannel.js +103 -0
  303. package/dist/pty-sidechannel.js.map +1 -0
  304. package/dist/scope-compiler.d.ts +52 -0
  305. package/dist/scope-compiler.d.ts.map +1 -0
  306. package/dist/scope-compiler.js +62 -0
  307. package/dist/scope-compiler.js.map +1 -0
  308. package/dist/slack-preflight.d.ts +6 -0
  309. package/dist/slack-preflight.d.ts.map +1 -0
  310. package/dist/slack-preflight.js +28 -0
  311. package/dist/slack-preflight.js.map +1 -0
  312. package/dist/slack-writeback.d.ts +40 -0
  313. package/dist/slack-writeback.d.ts.map +1 -0
  314. package/dist/slack-writeback.js +60 -0
  315. package/dist/slack-writeback.js.map +1 -0
  316. package/dist/spec.d.ts +134 -4
  317. package/dist/spec.d.ts.map +1 -1
  318. package/dist/spec.js.map +1 -1
  319. package/dist/step-dependencies.d.ts.map +1 -1
  320. package/dist/step-dependencies.js +2 -1
  321. package/dist/step-dependencies.js.map +1 -1
  322. package/dist/step-fields.d.ts +4 -4
  323. package/dist/step-fields.d.ts.map +1 -1
  324. package/dist/step-fields.js +5 -2
  325. package/dist/step-fields.js.map +1 -1
  326. package/dist/trigger-executor.d.ts +7 -0
  327. package/dist/trigger-executor.d.ts.map +1 -0
  328. package/dist/trigger-executor.js +35 -0
  329. package/dist/trigger-executor.js.map +1 -0
  330. package/dist/validate.d.ts.map +1 -1
  331. package/dist/validate.js +53 -6
  332. package/dist/validate.js.map +1 -1
  333. package/dist/webhook-rate-limit.d.ts +29 -0
  334. package/dist/webhook-rate-limit.d.ts.map +1 -0
  335. package/dist/webhook-rate-limit.js +38 -0
  336. package/dist/webhook-rate-limit.js.map +1 -0
  337. package/dist/webhook-signature.d.ts +22 -0
  338. package/dist/webhook-signature.d.ts.map +1 -0
  339. package/dist/webhook-signature.js +53 -0
  340. package/dist/webhook-signature.js.map +1 -0
  341. package/dist/worker-cli.d.ts +13 -1
  342. package/dist/worker-cli.d.ts.map +1 -1
  343. package/dist/worker-cli.js +133 -12
  344. package/dist/worker-cli.js.map +1 -1
  345. package/dist/worker-input.d.ts +4 -0
  346. package/dist/worker-input.d.ts.map +1 -0
  347. package/dist/worker-input.js +14 -0
  348. package/dist/worker-input.js.map +1 -0
  349. package/dist/worker-lease.d.ts +5 -0
  350. package/dist/worker-lease.d.ts.map +1 -0
  351. package/dist/worker-lease.js +80 -0
  352. package/dist/worker-lease.js.map +1 -0
  353. package/dist/worker-spend.d.ts +19 -0
  354. package/dist/worker-spend.d.ts.map +1 -0
  355. package/dist/worker-spend.js +22 -0
  356. package/dist/worker-spend.js.map +1 -0
  357. package/dist/worker-usage.d.ts +7 -0
  358. package/dist/worker-usage.d.ts.map +1 -0
  359. package/dist/worker-usage.js +54 -0
  360. package/dist/worker-usage.js.map +1 -0
  361. package/dist/worker.d.ts +2 -0
  362. package/dist/worker.d.ts.map +1 -1
  363. package/dist/worker.js +22 -8
  364. package/dist/worker.js.map +1 -1
  365. package/dist/wrapper-session.d.ts +1 -1
  366. package/dist/wrapper-session.d.ts.map +1 -1
  367. package/dist/wrapper-session.js +56 -19
  368. package/dist/wrapper-session.js.map +1 -1
  369. package/dist/yaml-helper-effect.d.ts +8 -0
  370. package/dist/yaml-helper-effect.d.ts.map +1 -0
  371. package/dist/yaml-helper-effect.js +79 -0
  372. package/dist/yaml-helper-effect.js.map +1 -0
  373. package/dist/yaml-helpers.d.ts +18 -0
  374. package/dist/yaml-helpers.d.ts.map +1 -0
  375. package/dist/yaml-helpers.js +116 -0
  376. package/dist/yaml-helpers.js.map +1 -0
  377. package/package.json +19 -2
  378. package/src/adapters/base.ts +53 -0
  379. package/src/adapters/claude.ts +55 -0
  380. package/src/adapters/codex.ts +56 -0
  381. package/src/adapters/index.ts +36 -0
  382. package/src/adapters/wrapper.ts +42 -0
  383. package/src/agent-relay-transport.ts +187 -0
  384. package/src/authored-budget.ts +69 -0
  385. package/src/authored-flow-error.ts +10 -0
  386. package/src/authored-flow-executor.ts +170 -293
  387. package/src/authored-flow-loader.ts +60 -3
  388. package/src/authored-flow-operation.ts +38 -5
  389. package/src/authored-helper-effect.ts +143 -0
  390. package/src/authored-mcp.ts +166 -0
  391. package/src/authored-memory.ts +55 -0
  392. package/src/authored-plugin-effect.ts +152 -0
  393. package/src/authored-step-output.ts +107 -0
  394. package/src/authored-worker-step.ts +181 -0
  395. package/src/budget-preflight.ts +31 -0
  396. package/src/budget.ts +49 -0
  397. package/src/bundle-transport.ts +79 -0
  398. package/src/bundle-typescript.ts +98 -0
  399. package/src/bundle.ts +182 -0
  400. package/src/child-stop.ts +138 -0
  401. package/src/cli/add.ts +45 -0
  402. package/src/cli/build.ts +214 -0
  403. package/src/cli/bundle-preflight.ts +28 -0
  404. package/src/cli/check-helper-body.ts +14 -0
  405. package/src/cli/check-triggers.ts +41 -0
  406. package/src/cli/check-typescript.ts +63 -0
  407. package/src/cli/check.ts +132 -14
  408. package/src/cli/cloud-run.ts +47 -0
  409. package/src/cli/deploy.ts +52 -0
  410. package/src/cli/deterministic-failure.ts +69 -0
  411. package/src/cli/direct-run.ts +79 -7
  412. package/src/cli/hn-monitor.ts +2 -1
  413. package/src/cli/replay.ts +81 -0
  414. package/src/cli/reuse.ts +23 -0
  415. package/src/cli/run-digest.ts +24 -0
  416. package/src/cli/run.ts +104 -21
  417. package/src/cli/serve-webhook.ts +246 -0
  418. package/src/cli/tick-runner.ts +2 -1
  419. package/src/cli-adapter.ts +31 -74
  420. package/src/cli-watch.ts +174 -0
  421. package/src/cli.ts +396 -17
  422. package/src/cloud-http.ts +100 -0
  423. package/src/cloud-run.ts +137 -0
  424. package/src/compile.ts +102 -23
  425. package/src/create-flow.ts +82 -0
  426. package/src/daemon-connection.ts +32 -1
  427. package/src/demo-hn-monitor.ts +2 -1
  428. package/src/effect-channel.ts +97 -0
  429. package/src/failure-kinds.ts +38 -0
  430. package/src/gate-contract.ts +7 -0
  431. package/src/helper-preflight.ts +34 -0
  432. package/src/helper-storage.ts +23 -0
  433. package/src/helper-writeback.ts +83 -0
  434. package/src/index.ts +26 -0
  435. package/src/input-binding.ts +76 -0
  436. package/src/journal-client.ts +20 -7
  437. package/src/journal-offset.ts +60 -0
  438. package/src/journal-reader.ts +165 -0
  439. package/src/json-schema.ts +34 -18
  440. package/src/llm-worker.ts +82 -0
  441. package/src/local-agent.ts +30 -0
  442. package/src/mcp-client.ts +97 -0
  443. package/src/mcp-config.ts +42 -0
  444. package/src/mcp-stdio.ts +84 -0
  445. package/src/model-pricing.ts +32 -0
  446. package/src/mount-registry.ts +44 -0
  447. package/src/named-gate-lowering.ts +147 -0
  448. package/src/named-gates.ts +76 -0
  449. package/src/observer-link.ts +368 -0
  450. package/src/plugin-loader.ts +88 -0
  451. package/src/plugin-manifest.ts +76 -0
  452. package/src/preflight.ts +178 -4
  453. package/src/progress.ts +51 -0
  454. package/src/protocol.ts +12 -0
  455. package/src/pty-sidechannel.ts +92 -0
  456. package/src/scope-compiler.ts +105 -0
  457. package/src/slack-preflight.ts +25 -0
  458. package/src/slack-writeback.ts +63 -0
  459. package/src/spec.ts +112 -4
  460. package/src/step-dependencies.ts +3 -1
  461. package/src/step-fields.ts +5 -2
  462. package/src/trigger-executor.ts +34 -0
  463. package/src/validate.ts +47 -6
  464. package/src/webhook-rate-limit.ts +69 -0
  465. package/src/webhook-signature.ts +74 -0
  466. package/src/worker-cli.ts +156 -11
  467. package/src/worker-input.ts +15 -0
  468. package/src/worker-lease.ts +85 -0
  469. package/src/worker-spend.ts +23 -0
  470. package/src/worker-usage.ts +48 -0
  471. package/src/worker.ts +27 -9
  472. package/src/wrapper-session.ts +50 -15
  473. package/src/yaml-helper-effect.ts +78 -0
  474. package/src/yaml-helpers.ts +131 -0
@@ -0,0 +1,63 @@
1
+ import { slackClient } from '@relayfile/relay-helpers';
2
+ import type { RelayTransport } from '@relayfile/relay-helpers/transport';
3
+ import type { SlackHelper } from '@relayflows/surface';
4
+ import { helperTransport } from './helper-writeback.js';
5
+ export { atomicJson, receiptPath, readHelperReceipt as readSlackReceipt } from './helper-storage.js';
6
+
7
+ export type SlackCall =
8
+ | { type: 'effect'; provider: 'slack'; verb: 'post'; params: { channel: string; text: Parameters<SlackHelper['post']>[1]; opts?: Parameters<SlackHelper['post']>[2] } }
9
+ | { type: 'effect'; provider: 'slack'; verb: 'dm'; params: { user: string; text: string } }
10
+ | { type: 'effect'; provider: 'slack'; verb: 'reply'; params: { channel: string; threadTs: string; text: string } }
11
+ | { type: 'effect'; provider: 'slack'; verb: 'react'; params: { channel: string; messageTs: string; emoji: string } };
12
+
13
+ interface SlackPostBodyExtras { text?: string; dropText: boolean; blocks?: unknown; attachments?: unknown }
14
+
15
+ // Slice Y widened SlackPostMessage to carry blocks/attachments; the transport
16
+ // body is otherwise built by slackClient, which only sees the flattened text.
17
+ // Reconstruct the intended body by projecting from the original call.params.
18
+ function slackPostBodyExtras(params: Extract<SlackCall, { verb: 'post' }>['params']): SlackPostBodyExtras {
19
+ const extras: SlackPostBodyExtras = { dropText: false };
20
+ const t = params.text;
21
+ const opts = params.opts as { blocks?: unknown; attachments?: unknown } | undefined;
22
+ if (typeof t === 'object' && t !== null) {
23
+ if ('text' in t && typeof t.text === 'string') extras.text = t.text;
24
+ else extras.dropText = true;
25
+ if ('blocks' in t && t.blocks !== undefined) extras.blocks = t.blocks;
26
+ if ('attachments' in t && t.attachments !== undefined) extras.attachments = t.attachments;
27
+ }
28
+ if (opts?.blocks !== undefined) extras.blocks = opts.blocks;
29
+ if (opts?.attachments !== undefined) extras.attachments = opts.attachments;
30
+ return extras;
31
+ }
32
+
33
+ function wrapSlackPostTransport(transport: RelayTransport, extras: SlackPostBodyExtras): RelayTransport {
34
+ return {
35
+ read: transport.read.bind(transport),
36
+ list: transport.list.bind(transport),
37
+ async write(request) {
38
+ const body = { ...request.body as Record<string, unknown> };
39
+ if (extras.dropText) delete body.text;
40
+ else if (extras.text !== undefined) body.text = extras.text;
41
+ if (extras.blocks !== undefined) body.blocks = extras.blocks;
42
+ if (extras.attachments !== undefined) body.attachments = extras.attachments;
43
+ return transport.write({ ...request, body });
44
+ },
45
+ };
46
+ }
47
+
48
+ export async function slackWriteback(
49
+ call: SlackCall, dataDir: string, runId: string, stepId: string, signal: AbortSignal,
50
+ ): Promise<unknown> {
51
+ const { transport, deliveredRef } = helperTransport(call, dataDir, runId, stepId, signal);
52
+ const effectiveTransport = call.verb === 'post'
53
+ ? wrapSlackPostTransport(transport, slackPostBodyExtras(call.params))
54
+ : transport;
55
+ const client = slackClient({ transport: effectiveTransport });
56
+ switch (call.verb) {
57
+ case 'post': return client.post(call.params.channel,
58
+ typeof call.params.text === 'string' ? call.params.text : call.params.text.text ?? '', call.params.opts);
59
+ case 'dm': return client.dm(call.params.user, call.params.text);
60
+ case 'reply': return { ...await client.reply(call.params.channel, call.params.threadTs, call.params.text), ref: deliveredRef() };
61
+ case 'react': await client.react(call.params.channel, call.params.messageTs, call.params.emoji); return null;
62
+ }
63
+ }
package/src/spec.ts CHANGED
@@ -15,11 +15,24 @@ export type { JsonOutputSchema } from './output-schema.js';
15
15
  /** The three rungs of the ladder (RFC §1; AGENTS.md rule 7). */
16
16
  export type StepType = 'deterministic' | 'llm' | 'agent';
17
17
 
18
+ /** Project-owned MCP connections. env contains names, never secret values. */
19
+ export type McpServerConfig =
20
+ | { command: string; args?: string[]; env?: string[] }
21
+ | { url: string; headers?: Record<string, string> };
22
+
23
+ export interface FlowsJson {
24
+ deploy?: { bucket: string };
25
+ cli?: string;
26
+ executors?: string[];
27
+ models?: string[];
28
+ mcp?: Record<string, McpServerConfig>;
29
+ }
30
+
18
31
  /**
19
32
  * Verification is control flow, not decoration (kernel DESIGN.md §3).
20
33
  * v0 gates are deterministic so verification is kernel-side and replayable.
21
34
  */
22
- export type VerificationGateType = 'exit_code' | 'output_contains' | 'json_schema';
35
+ export type VerificationGateType = VerificationSpec['type'];
23
36
 
24
37
  /**
25
38
  * `exit_code == 0` — the implicit gate for deterministic steps. v0 judges
@@ -43,8 +56,35 @@ export interface JsonSchemaGate {
43
56
  schema: boolean | Record<string, unknown>;
44
57
  }
45
58
 
46
- export type VerificationSpec = ExitCodeGate | OutputContainsGate | JsonSchemaGate;
47
- export type OutputVerificationSpec = OutputContainsGate | JsonSchemaGate;
59
+ export interface ReferencesInputGate {
60
+ type: 'references_input';
61
+ input_key: string;
62
+ in_output_at?: Array<string | number>;
63
+ }
64
+
65
+ export interface SubprocessGate {
66
+ type: 'subprocess_gate';
67
+ command: string;
68
+ from_output?: Array<string | number>;
69
+ }
70
+
71
+ export interface WordCountBoundsGate {
72
+ type: 'word_count_bounds';
73
+ min?: number;
74
+ max?: number;
75
+ }
76
+
77
+ export interface RegexMatchGate {
78
+ type: 'regex_match';
79
+ pattern: string;
80
+ in_output_at?: Array<string | number>;
81
+ /** Only i, m, and s; evaluated by a non-backtracking RE2 engine. */
82
+ flags?: string;
83
+ }
84
+
85
+ export type NamedDataGate = ReferencesInputGate | SubprocessGate | WordCountBoundsGate | RegexMatchGate;
86
+ export type OutputVerificationSpec = OutputContainsGate | JsonSchemaGate | NamedDataGate;
87
+ export type VerificationSpec = ExitCodeGate | OutputVerificationSpec;
48
88
 
49
89
  /**
50
90
  * Agent-step recovery modes (RFC Appendix A rule 4). Default is `reset`.
@@ -88,6 +128,11 @@ export interface PermissionsSpec {
88
128
  * are integers.
89
129
  */
90
130
  export interface BudgetSpec {
131
+ /** Set when normalizing the surface budget header. Legacy explicit envelopes accept worker-supplied prices. */
132
+ pricing?: 'frozen';
133
+ maxTokens?: number;
134
+ maxWallclockMs?: number;
135
+ window?: 'day';
91
136
  maxTokensIn?: number;
92
137
  maxTokensOut?: number;
93
138
  /** Decimal string, e.g. "1.50". */
@@ -124,6 +169,8 @@ export interface KernelPlacementRequirements extends Omit<PlacementRequirements,
124
169
 
125
170
  /** Fields shared by every step on the ladder. */
126
171
  export interface BaseStepSpec {
172
+ /** Named values selected from earlier steps' declared, verified outputs. */
173
+ input?: Record<string, OutputBinding>;
127
174
  requirements?: PlacementRequirements;
128
175
  memory?: MemorySpec;
129
176
  /** Stable step identity; journaled as `step_id` and hashed into the idempotency key. */
@@ -135,6 +182,12 @@ export interface BaseStepSpec {
135
182
  maxIterations?: number;
136
183
  }
137
184
 
185
+ export interface OutputBinding {
186
+ step: string;
187
+ /** Object keys or array indices; omit to select the whole output. */
188
+ path?: Array<string | number>;
189
+ }
190
+
138
191
  /**
139
192
  * Rung 1 — a pure script. Executed by the `relayflowd` binary: spawn command,
140
193
  * capture stdout/exit code. Output = `{exit_code, stdout_tail}`. Gate-1
@@ -145,6 +198,8 @@ export interface DeterministicStepSpec extends BaseStepSpec {
145
198
  command: string;
146
199
  /** Wall-clock command timeout; worker-backed verbs own their dispatch timeout. */
147
200
  timeoutMs?: number;
201
+ /** Per-invocation deterministic lease in milliseconds (maximum 15 minutes). */
202
+ lease_ms?: number;
148
203
  /** Omit to get the implicit `exit_code` gate. */
149
204
  verification?: VerificationSpec;
150
205
  }
@@ -192,6 +247,14 @@ export interface AgentStepSpec extends BaseStepSpec {
192
247
  surfaces?: AgentSurfaces;
193
248
  recoveryMode?: RecoveryMode;
194
249
  permissions?: PermissionsSpec;
250
+ /** Working directory for the CLI subprocess; defaults to the flow-runner's cwd. */
251
+ cwd?: string;
252
+ /**
253
+ * Dispatch transport (flows#385). `'direct'` (default) spawns the CLI as
254
+ * a local subprocess. `'relay'` posts to agent-relay so the agent
255
+ * registers as a first-class workspace participant.
256
+ */
257
+ transport?: 'direct' | 'relay';
195
258
  /**
196
259
  * Structured-output authoring sugar. A successful CLI JSON object is the parsed
197
260
  * value; the kernel persists it only after `json_schema` verification.
@@ -201,6 +264,33 @@ export interface AgentStepSpec extends BaseStepSpec {
201
264
 
202
265
  export type StepSpec = DeterministicStepSpec | LlmStepSpec | AgentStepSpec;
203
266
 
267
+ /** YAML argument maps use the same pinned client types as the TS helpers. */
268
+ export interface YamlHelperParams {
269
+ slack: { [V in import('./slack-writeback.js').SlackCall['verb']]:
270
+ Extract<import('./slack-writeback.js').SlackCall, { verb: V }>['params'] };
271
+ github: {
272
+ comment: { target: Parameters<import('@relayfile/relay-helpers').GithubClient['comment']>[0]; body: string };
273
+ createIssue: Parameters<import('@relayfile/relay-helpers').GithubClient['createIssue']>[0];
274
+ createPullRequest: Parameters<import('@relayfile/relay-helpers').GithubClient['createPullRequest']>[0];
275
+ closePullRequest: Parameters<import('@relayfile/relay-helpers').GithubClient['closePullRequest']>[0];
276
+ };
277
+ linear: {
278
+ comment: { issueId: string; body: string };
279
+ createIssue: Parameters<import('@relayfile/relay-helpers').LinearClient['createIssue']>[0];
280
+ updateIssue: { issueId: string; args: Parameters<import('@relayfile/relay-helpers').LinearClient['updateIssue']>[1] };
281
+ };
282
+ }
283
+
284
+ type OneKey<T> = { [K in keyof T]: Pick<T, K> & Partial<Record<Exclude<keyof T, K>, never>> }[keyof T];
285
+
286
+ /** Helper sugar is removed before validation of the three kernel step types. */
287
+ export type YamlHelperStepSpec = Pick<BaseStepSpec, 'id' | 'dependsOn' | 'maxIterations'> & {
288
+ verification?: OutputVerificationSpec;
289
+ output?: JsonOutputSchema;
290
+ } & OneKey<{ [P in keyof YamlHelperParams]: OneKey<YamlHelperParams[P]> }>;
291
+
292
+ export type YamlFlowSpec = Omit<FlowSpec, 'steps'> & { steps: Array<StepSpec | YamlHelperStepSpec> };
293
+
204
294
  /**
205
295
  * Reusable authoring declaration for an agent CLI/model pair. Both fields are
206
296
  * required so selecting a named agent can never inherit a host model. The
@@ -268,7 +358,11 @@ export interface FlowSpec {
268
358
  /** Declarations checked by preflight; gate 1 never dispatches them. */
269
359
  triggers?: TriggerSpec[];
270
360
  steps: StepSpec[];
271
- budget?: BudgetSpec;
361
+ budget?: BudgetSpec | import('./budget.js').HeaderBudget;
362
+ /** Path-scoped workspace grants — "mount/path: readonly|readwrite|append". Compiled by preflight. */
363
+ workspace?: string | readonly string[];
364
+ /** Path-scoped tool grants; `fs` mirrors workspace for shell/deterministic scope. */
365
+ tools?: { fs?: string | readonly string[] };
272
366
  }
273
367
 
274
368
  /** Current spec schema version emitted by this SDK. */
@@ -302,6 +396,7 @@ export interface KernelVerificationSpec {
302
396
  }
303
397
 
304
398
  export interface KernelStepCommon {
399
+ input?: Record<string, OutputBinding>;
305
400
  requirements?: KernelPlacementRequirements;
306
401
  memory?: KernelMemorySpec;
307
402
  id: string;
@@ -315,6 +410,7 @@ export interface KernelDeterministicStep extends KernelStepCommon {
315
410
  type: 'deterministic';
316
411
  command: string;
317
412
  timeout_ms?: number;
413
+ lease_ms?: number;
318
414
  }
319
415
 
320
416
  export interface KernelLlmStep extends KernelStepCommon {
@@ -344,11 +440,23 @@ export interface KernelAgentStep extends KernelStepCommon {
344
440
  recovery_mode: RecoveryMode;
345
441
  surfaces?: KernelAgentSurfaces;
346
442
  permissions?: KernelPermissionsSpec;
443
+ /** Working directory for the CLI subprocess; kernel passes through untouched. */
444
+ cwd?: string;
445
+ /**
446
+ * Dispatch transport (flows#385). Kernel passes through untouched;
447
+ * the SDK worker selects between direct-spawn and agent-relay routing.
448
+ */
449
+ transport?: 'direct' | 'relay';
347
450
  }
348
451
 
349
452
  export type KernelStepSpec = KernelDeterministicStep | KernelLlmStep | KernelAgentStep;
350
453
 
351
454
  export interface KernelBudgetSpec {
455
+ pricing?: 'frozen';
456
+ prior_spend?: { tokens_in: number; tokens_out: number; dollars: string; wallclock_ms: number; day?: number };
457
+ max_tokens?: number;
458
+ max_wallclock_ms?: number;
459
+ window?: 'day';
352
460
  max_tokens_in?: number;
353
461
  max_tokens_out?: number;
354
462
  max_dollars?: string;
@@ -1,3 +1,5 @@
1
+ import { bindingDependencies } from './input-binding.js';
2
+
1
3
  /** Return author-facing dependency errors without assuming parsed step shapes. */
2
4
  const MAX_REPORTED_CYCLE_PATH_IDS = 16;
3
5
 
@@ -21,7 +23,7 @@ export function stepDependencyErrors(
21
23
  }
22
24
 
23
25
  const id = value['id'];
24
- const dependencies = (rawDependencies ?? []) as string[];
26
+ const dependencies = [...(rawDependencies ?? []) as string[], ...bindingDependencies(value['input'])];
25
27
  for (const dependency of dependencies) {
26
28
  if (!knownIds.has(dependency)) {
27
29
  errors.push(`spec.steps: step "${id}" dependsOn unknown step "${dependency}"`);
@@ -10,6 +10,8 @@ export const FLOW_FIELDS = [
10
10
  'triggers',
11
11
  'steps',
12
12
  'budget',
13
+ 'workspace',
14
+ 'tools',
13
15
  ] as const;
14
16
 
15
17
  /** Closed named-agent declaration schema. */
@@ -20,6 +22,7 @@ export const STEP_COMMON_FIELDS = [
20
22
  'id',
21
23
  'type',
22
24
  'dependsOn',
25
+ 'input',
23
26
  'verification',
24
27
  'maxIterations',
25
28
  'memory',
@@ -32,7 +35,7 @@ export const STEP_COMMON_FIELDS = [
32
35
  * per-verb boundary through a second, drifting allowlist.
33
36
  */
34
37
  export const STEP_FIELDS_BY_TYPE = {
35
- deterministic: ['command', 'timeoutMs'],
38
+ deterministic: ['command', 'timeoutMs', 'lease_ms'],
36
39
  llm: ['prompt', 'model', 'cli', 'output'],
37
- agent: ['instruction', 'agent', 'cli', 'model', 'surfaces', 'recoveryMode', 'permissions', 'output'],
40
+ agent: ['instruction', 'agent', 'cli', 'model', 'cwd', 'transport', 'surfaces', 'recoveryMode', 'permissions', 'output'],
38
41
  } as const satisfies Record<StepType, readonly string[]>;
@@ -0,0 +1,34 @@
1
+ import { providerEventTypes, webhook, type TriggerSource, type WebhookFilter } from '@relayflows/surface';
2
+ import type { TriggerSpec } from './spec.js';
3
+
4
+ /** Lower a surface subscription to the existing inbox executor contract. */
5
+ export function webhookTriggerSpec(id: string, source: TriggerSource): TriggerSpec {
6
+ if (source.kind !== 'webhook') throw new TypeError('unsupported trigger kind');
7
+ const trigger = webhook(source.name, source.filter);
8
+ return {
9
+ id,
10
+ executor: trigger.name,
11
+ eventType: trigger.name,
12
+ ...(trigger.filter === undefined ? {} : { pattern: trigger.filter }),
13
+ // The inbox watcher supplies the durable file ID as the event key.
14
+ dedupeKeyTemplate: '{{event.type}}',
15
+ };
16
+ }
17
+
18
+ /** Provider ingress uses the path as authority; it never trusts a body to reroute. */
19
+ export function providerInboxEvent(provider: string, value: unknown): WebhookFilter {
20
+ if (!Object.hasOwn(providerEventTypes, provider)) throw new TypeError(`unknown provider: ${provider}`);
21
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
22
+ throw new TypeError('provider event must be an object');
23
+ }
24
+ const event = value as Record<string, unknown>;
25
+ const types: readonly string[] = providerEventTypes[provider as keyof typeof providerEventTypes];
26
+ if (typeof event.type !== 'string' || !types.includes(event.type)) {
27
+ throw new TypeError(`unknown event type for ${provider}`);
28
+ }
29
+ if (event.provider !== undefined && event.provider !== provider) throw new TypeError('provider does not match inbox');
30
+ if (event.payload === null || typeof event.payload !== 'object' || Array.isArray(event.payload)) {
31
+ throw new TypeError('provider event payload must be an object');
32
+ }
33
+ return webhook(provider, { ...event, provider } as WebhookFilter).filter!;
34
+ }
package/src/validate.ts CHANGED
@@ -21,6 +21,8 @@ import { validateOutputDeclaration } from './output-schema.js';
21
21
  import { modelNameError } from './model-name.js';
22
22
  import { unknownKeyErrors } from './unknown-keys.js';
23
23
  import { stepDependencyErrors } from './step-dependencies.js';
24
+ import { inputBindingErrors } from './input-binding.js';
25
+ import { NAMED_GATE_KEYS, namedGateErrors } from './named-gates.js';
24
26
  import {
25
27
  AGENT_DECLARATION_FIELDS,
26
28
  FLOW_FIELDS,
@@ -68,8 +70,9 @@ function isCanonicalPathSurface(value: unknown): value is string {
68
70
  // unknown keys (AGENTS.md rule 4; RFC covenant 2): a typo'd key like
69
71
  // `depends_on` must be an error naming the nearest valid key, never a
70
72
  // silently discarded field — silently dropping `dependsOn` loses ordering.
71
- const BUDGET_KEYS = ['maxTokensIn', 'maxTokensOut', 'maxDollars'] as const;
73
+ const BUDGET_KEYS = ['maxTokensIn', 'maxTokensOut', 'maxDollars', 'maxTokens', 'maxWallclockMs', 'window', 'pricing'] as const;
72
74
  const VERIFICATION_KEYS: Record<string, readonly string[]> = {
75
+ ...NAMED_GATE_KEYS,
73
76
  exit_code: ['type', 'expect'],
74
77
  output_contains: ['type', 'value'],
75
78
  json_schema: ['type', 'schema'],
@@ -163,6 +166,22 @@ class Validator {
163
166
 
164
167
  if (s['budget'] !== undefined) this.validateBudget(s['budget']);
165
168
 
169
+ if (s['workspace'] !== undefined) this.validateScopeGrants(s['workspace'], 'spec.workspace');
170
+
171
+ if (s['tools'] !== undefined) {
172
+ if (!isObject(s['tools'])) {
173
+ this.fail('spec.tools: expected an object');
174
+ } else {
175
+ const tools = s['tools'] as Record<string, unknown>;
176
+ for (const key of Object.keys(tools)) {
177
+ if (key !== 'fs' && key !== 'mcp') {
178
+ this.fail(`spec.tools: unknown key "${key}" (expected fs or mcp)`);
179
+ }
180
+ }
181
+ if (tools['fs'] !== undefined) this.validateScopeGrants(tools['fs'], 'spec.tools.fs');
182
+ }
183
+ }
184
+
166
185
  if (!Array.isArray(s['steps']) || s['steps'].length === 0) {
167
186
  this.fail('spec.steps: expected a non-empty array');
168
187
  return this.result();
@@ -175,6 +194,7 @@ class Validator {
175
194
 
176
195
  // Dependents must reference real step ids and form a DAG (no cycles).
177
196
  for (const error of stepDependencyErrors(steps, this.ids)) this.fail(error);
197
+ for (const error of inputBindingErrors(steps)) this.fail(error);
178
198
  return this.result();
179
199
  }
180
200
 
@@ -203,13 +223,28 @@ class Validator {
203
223
  }
204
224
  }
205
225
 
226
+ private validateScopeGrants(value: unknown, at: string): void {
227
+ const grants = Array.isArray(value) ? value : [value];
228
+ for (const grant of grants) {
229
+ if (typeof grant !== 'string' || grant.length === 0) {
230
+ this.fail(`${at}: expected a scope-grant string like "mount/path: readonly" (or an array of them)`);
231
+ return;
232
+ }
233
+ }
234
+ }
235
+
206
236
  private validateBudget(b: unknown, at = 'spec.budget'): void {
207
237
  if (!isObject(b)) {
208
238
  this.fail(`${at}: expected an object`);
209
239
  return;
210
240
  }
211
- this.checkKeys(b, BUDGET_KEYS, at);
241
+ this.checkKeys(b, at === 'spec.budget' ? BUDGET_KEYS : ['maxTokensIn', 'maxTokensOut', 'maxDollars'], at);
212
242
  const budget = b as BudgetSpec;
243
+ for (const key of ['maxTokens', 'maxWallclockMs'] as const) {
244
+ if (budget[key] !== undefined && (!Number.isSafeInteger(budget[key]) || budget[key]! < 0)) this.fail(`${at}.${key}: expected a safe non-negative integer`);
245
+ }
246
+ if (budget.pricing !== undefined && budget.pricing !== 'frozen') this.fail(`${at}.pricing: expected frozen`);
247
+ if (budget.window !== undefined && budget.window !== 'day') this.fail(`${at}.window: expected day`);
213
248
  if (
214
249
  budget.maxTokensIn !== undefined &&
215
250
  !isNonNegInt(budget.maxTokensIn)
@@ -356,7 +391,7 @@ class Validator {
356
391
  }
357
392
 
358
393
  if (st['verification'] !== undefined) {
359
- this.validateVerification(st['verification'], `${at}.verification`, type);
394
+ this.validateVerification(st['verification'], `${at}.verification`, type, st['input']);
360
395
  }
361
396
 
362
397
  if (st['maxIterations'] !== undefined && !isPosInt(st['maxIterations'])) {
@@ -374,13 +409,14 @@ class Validator {
374
409
  }
375
410
  }
376
411
 
377
- private validateVerification(v: unknown, at: string, stepType: StepType): void {
412
+ private validateVerification(v: unknown, at: string, stepType: StepType, input: unknown): void {
378
413
  if (!isObject(v)) {
379
414
  this.fail(`${at}: expected an object`);
380
415
  return;
381
416
  }
382
417
  const gate = v as unknown as VerificationSpec & { expect?: unknown };
383
- const gateKeys = typeof gate.type === 'string' ? VERIFICATION_KEYS[gate.type] : undefined;
418
+ const gateKeys = typeof gate.type === 'string' && Object.hasOwn(VERIFICATION_KEYS, gate.type)
419
+ ? VERIFICATION_KEYS[gate.type] : undefined;
384
420
  if (gateKeys !== undefined) {
385
421
  this.checkKeys(v, gateKeys, at);
386
422
  }
@@ -407,8 +443,10 @@ class Validator {
407
443
  } catch (error) {
408
444
  this.fail(error instanceof Error ? error.message : `${at}.schema: expected JSON-compatible data`);
409
445
  }
446
+ } else if (Object.hasOwn(NAMED_GATE_KEYS, gate.type)) {
447
+ for (const error of namedGateErrors(v, input, at)) this.fail(error);
410
448
  } else {
411
- this.fail(`${at}.type: expected exit_code | output_contains | json_schema`);
449
+ this.fail(`${at}.type: unknown_gate_kind: expected exit_code | output_contains | json_schema | references_input | subprocess_gate | word_count_bounds | regex_match`);
412
450
  }
413
451
  }
414
452
 
@@ -416,6 +454,9 @@ class Validator {
416
454
  if (!isNonEmptyString(st.command)) {
417
455
  this.fail(`${at}.command: expected a non-empty string`);
418
456
  }
457
+ if (st.lease_ms !== undefined && !isPosInt(st.lease_ms)) {
458
+ this.fail(`${at}.lease_ms: expected a positive integer`);
459
+ }
419
460
  if (st.timeoutMs !== undefined && !isPosInt(st.timeoutMs)) {
420
461
  this.fail(`${at}.timeoutMs: expected a positive integer`);
421
462
  }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * In-process token-bucket rate limiter for the local webhook receiver (#304).
3
+ *
4
+ * Slice E's receiver binds loopback and is documented as development-only, so
5
+ * a single-process in-memory bucket is sufficient. Cluster deployments must
6
+ * layer a shared store in a follow-up slice; the taxonomy stays the same
7
+ * (`webhook_rate_limited`) so client behavior does not change.
8
+ */
9
+ export interface RateLimitConfig {
10
+ /** Tokens replenished per second. */
11
+ readonly ratePerSecond: number;
12
+ /** Maximum burst capacity. */
13
+ readonly burst: number;
14
+ }
15
+
16
+ export interface RateLimitDecision {
17
+ readonly allowed: boolean;
18
+ readonly retryAfterMs: number;
19
+ }
20
+
21
+ interface Bucket {
22
+ tokens: number;
23
+ refilledAtMs: number;
24
+ }
25
+
26
+ export class TokenBucketLimiter {
27
+ private readonly buckets = new Map<string, Bucket>();
28
+
29
+ constructor(
30
+ private readonly config: RateLimitConfig,
31
+ private readonly clock: () => number = Date.now,
32
+ ) {
33
+ if (config.burst <= 0 || config.ratePerSecond < 0) {
34
+ throw new Error('rate limiter: burst > 0 and ratePerSecond >= 0 required');
35
+ }
36
+ }
37
+
38
+ consume(key: string, cost = 1): RateLimitDecision {
39
+ const now = this.clock();
40
+ const bucket = this.buckets.get(key) ?? { tokens: this.config.burst, refilledAtMs: now };
41
+ const elapsedSeconds = Math.max(0, (now - bucket.refilledAtMs) / 1000);
42
+ bucket.tokens = Math.min(this.config.burst, bucket.tokens + elapsedSeconds * this.config.ratePerSecond);
43
+ bucket.refilledAtMs = now;
44
+ if (bucket.tokens >= cost) {
45
+ bucket.tokens -= cost;
46
+ this.buckets.set(key, bucket);
47
+ return { allowed: true, retryAfterMs: 0 };
48
+ }
49
+ this.buckets.set(key, bucket);
50
+ const missingTokens = cost - bucket.tokens;
51
+ const retryAfterMs = this.config.ratePerSecond > 0
52
+ ? Math.ceil((missingTokens / this.config.ratePerSecond) * 1000)
53
+ : Number.POSITIVE_INFINITY;
54
+ return { allowed: false, retryAfterMs };
55
+ }
56
+
57
+ /** Test/inspection helper — production code should only call {@link consume}. */
58
+ size(): number {
59
+ return this.buckets.size;
60
+ }
61
+ }
62
+
63
+ export function keyFor(
64
+ provider: string | undefined,
65
+ name: string,
66
+ sourceAddress: string,
67
+ ): string {
68
+ return `${provider ?? 'raw'}:${name}:${sourceAddress}`;
69
+ }
@@ -0,0 +1,74 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+
3
+ /**
4
+ * Per-provider HMAC signature schemes for webhook payload authentication (#304).
5
+ *
6
+ * The receiver only invokes verification when a secret is configured for the
7
+ * provider (via `env[WEBHOOK_SECRET_<PROVIDER>]`). When no secret is set the
8
+ * receiver runs unsigned, matching the initial loopback-only development mode
9
+ * documented in #301. Callers deploying to public ingress MUST set the secret.
10
+ */
11
+ export type SignatureScheme = {
12
+ header: string;
13
+ compute(rawBody: Buffer, secret: string): string;
14
+ };
15
+
16
+ const HEX = /^[0-9a-f]+$/i;
17
+
18
+ const github: SignatureScheme = {
19
+ header: 'x-hub-signature-256',
20
+ compute(rawBody, secret) {
21
+ return 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
22
+ },
23
+ };
24
+
25
+ const slack: SignatureScheme = {
26
+ header: 'x-slack-signature',
27
+ compute(rawBody, secret) {
28
+ // Slack's v0 scheme is HMAC(SHA256, secret) over `v0:{ts}:{body}`; a
29
+ // deployment that wants Slack signature verification must set the secret
30
+ // to include the timestamp prefix or use provider-specific integration.
31
+ return 'v0=' + createHmac('sha256', secret).update(rawBody).digest('hex');
32
+ },
33
+ };
34
+
35
+ const generic: SignatureScheme = {
36
+ header: 'x-flows-signature-256',
37
+ compute(rawBody, secret) {
38
+ return 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
39
+ },
40
+ };
41
+
42
+ export const SIGNATURE_SCHEMES: Record<string, SignatureScheme> = {
43
+ github,
44
+ slack,
45
+ };
46
+
47
+ export function schemeFor(provider: string | undefined): SignatureScheme {
48
+ if (provider && Object.hasOwn(SIGNATURE_SCHEMES, provider)) {
49
+ return SIGNATURE_SCHEMES[provider]!;
50
+ }
51
+ return generic;
52
+ }
53
+
54
+ /** Constant-time verification of a hex-encoded signature against the expected. */
55
+ export function verifySignature(
56
+ provider: string | undefined,
57
+ rawBody: Buffer,
58
+ headerValue: string | undefined,
59
+ secret: string,
60
+ ): { ok: true } | { ok: false; reason: 'missing' | 'malformed' | 'mismatch' } {
61
+ const scheme = schemeFor(provider);
62
+ const supplied = headerValue?.trim();
63
+ if (!supplied) return { ok: false, reason: 'missing' };
64
+ const expected = scheme.compute(rawBody, secret);
65
+ if (supplied.length !== expected.length) return { ok: false, reason: 'malformed' };
66
+ const [suppliedHex, expectedHex] = [supplied.replace(/^[^=]+=/, ''), expected.replace(/^[^=]+=/, '')];
67
+ if (!HEX.test(suppliedHex) || suppliedHex.length !== expectedHex.length) {
68
+ return { ok: false, reason: 'malformed' };
69
+ }
70
+ const suppliedBytes = Buffer.from(suppliedHex, 'hex');
71
+ const expectedBytes = Buffer.from(expectedHex, 'hex');
72
+ if (suppliedBytes.length !== expectedBytes.length) return { ok: false, reason: 'malformed' };
73
+ return timingSafeEqual(suppliedBytes, expectedBytes) ? { ok: true } : { ok: false, reason: 'mismatch' };
74
+ }