@relayflows/surface 2.0.16 → 2.0.17

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 (271) hide show
  1. package/dist/context.d.ts +17 -0
  2. package/dist/context.d.ts.map +1 -1
  3. package/dist/flow.d.ts.map +1 -1
  4. package/dist/flow.js +19 -4
  5. package/dist/flow.js.map +1 -1
  6. package/dist/index.d.ts +3 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/provider-trigger.d.ts +2 -2
  11. package/dist/provider-trigger.d.ts.map +1 -1
  12. package/dist/provider-trigger.js.map +1 -1
  13. package/dist/schedule.d.ts +76 -0
  14. package/dist/schedule.d.ts.map +1 -0
  15. package/dist/schedule.js +264 -0
  16. package/dist/schedule.js.map +1 -0
  17. package/dist/step.d.ts +20 -6
  18. package/dist/step.d.ts.map +1 -1
  19. package/dist/triggers/airtable.d.ts +13 -0
  20. package/dist/triggers/airtable.d.ts.map +1 -0
  21. package/dist/triggers/airtable.js +32 -0
  22. package/dist/triggers/airtable.js.map +1 -0
  23. package/dist/triggers/asana.d.ts +21 -0
  24. package/dist/triggers/asana.d.ts.map +1 -0
  25. package/dist/triggers/asana.js +56 -0
  26. package/dist/triggers/asana.js.map +1 -0
  27. package/dist/triggers/azure-blob.d.ts +7 -0
  28. package/dist/triggers/azure-blob.d.ts.map +1 -0
  29. package/dist/triggers/azure-blob.js +14 -0
  30. package/dist/triggers/azure-blob.js.map +1 -0
  31. package/dist/triggers/box.d.ts +7 -0
  32. package/dist/triggers/box.d.ts.map +1 -0
  33. package/dist/triggers/box.js +14 -0
  34. package/dist/triggers/box.js.map +1 -0
  35. package/dist/triggers/calendly.d.ts +16 -0
  36. package/dist/triggers/calendly.d.ts.map +1 -0
  37. package/dist/triggers/calendly.js +41 -0
  38. package/dist/triggers/calendly.js.map +1 -0
  39. package/dist/triggers/clickup.d.ts +16 -0
  40. package/dist/triggers/clickup.d.ts.map +1 -0
  41. package/dist/triggers/clickup.js +41 -0
  42. package/dist/triggers/clickup.js.map +1 -0
  43. package/dist/triggers/cloudflare.d.ts +12 -0
  44. package/dist/triggers/cloudflare.d.ts.map +1 -0
  45. package/dist/triggers/cloudflare.js +29 -0
  46. package/dist/triggers/cloudflare.js.map +1 -0
  47. package/dist/triggers/confluence.d.ts +10 -0
  48. package/dist/triggers/confluence.d.ts.map +1 -0
  49. package/dist/triggers/confluence.js +23 -0
  50. package/dist/triggers/confluence.js.map +1 -0
  51. package/dist/triggers/daytona.d.ts +11 -0
  52. package/dist/triggers/daytona.d.ts.map +1 -0
  53. package/dist/triggers/daytona.js +26 -0
  54. package/dist/triggers/daytona.js.map +1 -0
  55. package/dist/triggers/docker-hub.d.ts +5 -0
  56. package/dist/triggers/docker-hub.d.ts.map +1 -0
  57. package/dist/triggers/docker-hub.js +8 -0
  58. package/dist/triggers/docker-hub.js.map +1 -0
  59. package/dist/triggers/dropbox.d.ts +5 -0
  60. package/dist/triggers/dropbox.d.ts.map +1 -0
  61. package/dist/triggers/dropbox.js +8 -0
  62. package/dist/triggers/dropbox.js.map +1 -0
  63. package/dist/triggers/fathom.d.ts +5 -0
  64. package/dist/triggers/fathom.d.ts.map +1 -0
  65. package/dist/triggers/fathom.js +8 -0
  66. package/dist/triggers/fathom.js.map +1 -0
  67. package/dist/triggers/gcp.d.ts +13 -0
  68. package/dist/triggers/gcp.d.ts.map +1 -0
  69. package/dist/triggers/gcp.js +32 -0
  70. package/dist/triggers/gcp.js.map +1 -0
  71. package/dist/triggers/gcs.d.ts +7 -0
  72. package/dist/triggers/gcs.d.ts.map +1 -0
  73. package/dist/triggers/gcs.js +14 -0
  74. package/dist/triggers/gcs.js.map +1 -0
  75. package/dist/triggers/github.d.ts +22 -0
  76. package/dist/triggers/github.d.ts.map +1 -1
  77. package/dist/triggers/github.js +66 -0
  78. package/dist/triggers/github.js.map +1 -1
  79. package/dist/triggers/gitlab.d.ts +57 -0
  80. package/dist/triggers/gitlab.d.ts.map +1 -0
  81. package/dist/triggers/gitlab.js +164 -0
  82. package/dist/triggers/gitlab.js.map +1 -0
  83. package/dist/triggers/gmail.d.ts +7 -0
  84. package/dist/triggers/gmail.d.ts.map +1 -0
  85. package/dist/triggers/gmail.js +14 -0
  86. package/dist/triggers/gmail.js.map +1 -0
  87. package/dist/triggers/google-calendar.d.ts +7 -0
  88. package/dist/triggers/google-calendar.d.ts.map +1 -0
  89. package/dist/triggers/google-calendar.js +14 -0
  90. package/dist/triggers/google-calendar.js.map +1 -0
  91. package/dist/triggers/google-drive.d.ts +7 -0
  92. package/dist/triggers/google-drive.d.ts.map +1 -0
  93. package/dist/triggers/google-drive.js +14 -0
  94. package/dist/triggers/google-drive.js.map +1 -0
  95. package/dist/triggers/granola.d.ts +8 -0
  96. package/dist/triggers/granola.d.ts.map +1 -0
  97. package/dist/triggers/granola.js +17 -0
  98. package/dist/triggers/granola.js.map +1 -0
  99. package/dist/triggers/hubspot.d.ts +24 -0
  100. package/dist/triggers/hubspot.d.ts.map +1 -0
  101. package/dist/triggers/hubspot.js +65 -0
  102. package/dist/triggers/hubspot.js.map +1 -0
  103. package/dist/triggers/index.d.ts +92 -2
  104. package/dist/triggers/index.d.ts.map +1 -1
  105. package/dist/triggers/index.js +92 -2
  106. package/dist/triggers/index.js.map +1 -1
  107. package/dist/triggers/intercom.d.ts +22 -0
  108. package/dist/triggers/intercom.d.ts.map +1 -0
  109. package/dist/triggers/intercom.js +59 -0
  110. package/dist/triggers/intercom.js.map +1 -0
  111. package/dist/triggers/jira.d.ts +16 -0
  112. package/dist/triggers/jira.d.ts.map +1 -0
  113. package/dist/triggers/jira.js +41 -0
  114. package/dist/triggers/jira.js.map +1 -0
  115. package/dist/triggers/linear.d.ts +37 -0
  116. package/dist/triggers/linear.d.ts.map +1 -0
  117. package/dist/triggers/linear.js +104 -0
  118. package/dist/triggers/linear.js.map +1 -0
  119. package/dist/triggers/mailgun.d.ts +19 -0
  120. package/dist/triggers/mailgun.d.ts.map +1 -0
  121. package/dist/triggers/mailgun.js +50 -0
  122. package/dist/triggers/mailgun.js.map +1 -0
  123. package/dist/triggers/mixpanel.d.ts +16 -0
  124. package/dist/triggers/mixpanel.d.ts.map +1 -0
  125. package/dist/triggers/mixpanel.js +41 -0
  126. package/dist/triggers/mixpanel.js.map +1 -0
  127. package/dist/triggers/neon.d.ts +9 -0
  128. package/dist/triggers/neon.d.ts.map +1 -0
  129. package/dist/triggers/neon.js +20 -0
  130. package/dist/triggers/neon.js.map +1 -0
  131. package/dist/triggers/notion.d.ts +17 -0
  132. package/dist/triggers/notion.d.ts.map +1 -0
  133. package/dist/triggers/notion.js +44 -0
  134. package/dist/triggers/notion.js.map +1 -0
  135. package/dist/triggers/onedrive.d.ts +7 -0
  136. package/dist/triggers/onedrive.d.ts.map +1 -0
  137. package/dist/triggers/onedrive.js +14 -0
  138. package/dist/triggers/onedrive.js.map +1 -0
  139. package/dist/triggers/pipedrive.d.ts +18 -0
  140. package/dist/triggers/pipedrive.d.ts.map +1 -0
  141. package/dist/triggers/pipedrive.js +47 -0
  142. package/dist/triggers/pipedrive.js.map +1 -0
  143. package/dist/triggers/postgres.d.ts +7 -0
  144. package/dist/triggers/postgres.d.ts.map +1 -0
  145. package/dist/triggers/postgres.js +14 -0
  146. package/dist/triggers/postgres.js.map +1 -0
  147. package/dist/triggers/posthog.d.ts +6 -0
  148. package/dist/triggers/posthog.d.ts.map +1 -0
  149. package/dist/triggers/posthog.js +11 -0
  150. package/dist/triggers/posthog.js.map +1 -0
  151. package/dist/triggers/ramp.d.ts +51 -0
  152. package/dist/triggers/ramp.d.ts.map +1 -0
  153. package/dist/triggers/ramp.js +146 -0
  154. package/dist/triggers/ramp.js.map +1 -0
  155. package/dist/triggers/recall.d.ts +7 -0
  156. package/dist/triggers/recall.d.ts.map +1 -0
  157. package/dist/triggers/recall.js +14 -0
  158. package/dist/triggers/recall.js.map +1 -0
  159. package/dist/triggers/redis.d.ts +7 -0
  160. package/dist/triggers/redis.d.ts.map +1 -0
  161. package/dist/triggers/redis.js +14 -0
  162. package/dist/triggers/redis.js.map +1 -0
  163. package/dist/triggers/s3.d.ts +7 -0
  164. package/dist/triggers/s3.d.ts.map +1 -0
  165. package/dist/triggers/s3.js +14 -0
  166. package/dist/triggers/s3.js.map +1 -0
  167. package/dist/triggers/salesforce.d.ts +26 -0
  168. package/dist/triggers/salesforce.d.ts.map +1 -0
  169. package/dist/triggers/salesforce.js +71 -0
  170. package/dist/triggers/salesforce.js.map +1 -0
  171. package/dist/triggers/segment.d.ts +16 -0
  172. package/dist/triggers/segment.d.ts.map +1 -0
  173. package/dist/triggers/segment.js +41 -0
  174. package/dist/triggers/segment.js.map +1 -0
  175. package/dist/triggers/sendgrid.d.ts +21 -0
  176. package/dist/triggers/sendgrid.d.ts.map +1 -0
  177. package/dist/triggers/sendgrid.js +56 -0
  178. package/dist/triggers/sendgrid.js.map +1 -0
  179. package/dist/triggers/sharepoint.d.ts +7 -0
  180. package/dist/triggers/sharepoint.d.ts.map +1 -0
  181. package/dist/triggers/sharepoint.js +14 -0
  182. package/dist/triggers/sharepoint.js.map +1 -0
  183. package/dist/triggers/shopify.d.ts +28 -0
  184. package/dist/triggers/shopify.d.ts.map +1 -0
  185. package/dist/triggers/shopify.js +77 -0
  186. package/dist/triggers/shopify.js.map +1 -0
  187. package/dist/triggers/shortcut.d.ts +10 -0
  188. package/dist/triggers/shortcut.d.ts.map +1 -0
  189. package/dist/triggers/shortcut.js +23 -0
  190. package/dist/triggers/shortcut.js.map +1 -0
  191. package/dist/triggers/slack.d.ts +17 -0
  192. package/dist/triggers/slack.d.ts.map +1 -1
  193. package/dist/triggers/slack.js +51 -0
  194. package/dist/triggers/slack.js.map +1 -1
  195. package/dist/triggers/stripe.d.ts +25 -0
  196. package/dist/triggers/stripe.d.ts.map +1 -0
  197. package/dist/triggers/stripe.js +68 -0
  198. package/dist/triggers/stripe.js.map +1 -0
  199. package/dist/triggers/teams.d.ts +24 -0
  200. package/dist/triggers/teams.d.ts.map +1 -0
  201. package/dist/triggers/teams.js +65 -0
  202. package/dist/triggers/teams.js.map +1 -0
  203. package/dist/triggers/telegram.d.ts +29 -0
  204. package/dist/triggers/telegram.d.ts.map +1 -0
  205. package/dist/triggers/telegram.js +80 -0
  206. package/dist/triggers/telegram.js.map +1 -0
  207. package/dist/triggers/zendesk.d.ts +13 -0
  208. package/dist/triggers/zendesk.d.ts.map +1 -0
  209. package/dist/triggers/zendesk.js +32 -0
  210. package/dist/triggers/zendesk.js.map +1 -0
  211. package/dist/triggers.d.ts +5 -2
  212. package/dist/triggers.d.ts.map +1 -1
  213. package/dist/triggers.js.map +1 -1
  214. package/package.json +1 -1
  215. package/src/context.ts +18 -0
  216. package/src/flow.ts +17 -3
  217. package/src/index.ts +6 -2
  218. package/src/provider-trigger.ts +2 -2
  219. package/src/schedule.ts +282 -0
  220. package/src/step.ts +22 -6
  221. package/src/triggers/PROVIDERS.md +56 -0
  222. package/src/triggers/README.md +84 -8
  223. package/src/triggers/airtable.ts +34 -0
  224. package/src/triggers/asana.ts +58 -0
  225. package/src/triggers/azure-blob.ts +16 -0
  226. package/src/triggers/box.ts +16 -0
  227. package/src/triggers/calendly.ts +43 -0
  228. package/src/triggers/clickup.ts +43 -0
  229. package/src/triggers/cloudflare.ts +31 -0
  230. package/src/triggers/confluence.ts +25 -0
  231. package/src/triggers/daytona.ts +28 -0
  232. package/src/triggers/docker-hub.ts +10 -0
  233. package/src/triggers/dropbox.ts +10 -0
  234. package/src/triggers/fathom.ts +10 -0
  235. package/src/triggers/gcp.ts +34 -0
  236. package/src/triggers/gcs.ts +16 -0
  237. package/src/triggers/github.ts +66 -0
  238. package/src/triggers/gitlab.ts +166 -0
  239. package/src/triggers/gmail.ts +16 -0
  240. package/src/triggers/google-calendar.ts +16 -0
  241. package/src/triggers/google-drive.ts +16 -0
  242. package/src/triggers/granola.ts +19 -0
  243. package/src/triggers/hubspot.ts +67 -0
  244. package/src/triggers/index.ts +92 -2
  245. package/src/triggers/intercom.ts +61 -0
  246. package/src/triggers/jira.ts +43 -0
  247. package/src/triggers/linear.ts +106 -0
  248. package/src/triggers/mailgun.ts +52 -0
  249. package/src/triggers/mixpanel.ts +43 -0
  250. package/src/triggers/neon.ts +22 -0
  251. package/src/triggers/notion.ts +46 -0
  252. package/src/triggers/onedrive.ts +16 -0
  253. package/src/triggers/pipedrive.ts +49 -0
  254. package/src/triggers/postgres.ts +16 -0
  255. package/src/triggers/posthog.ts +13 -0
  256. package/src/triggers/ramp.ts +148 -0
  257. package/src/triggers/recall.ts +16 -0
  258. package/src/triggers/redis.ts +16 -0
  259. package/src/triggers/s3.ts +16 -0
  260. package/src/triggers/salesforce.ts +73 -0
  261. package/src/triggers/segment.ts +43 -0
  262. package/src/triggers/sendgrid.ts +58 -0
  263. package/src/triggers/sharepoint.ts +16 -0
  264. package/src/triggers/shopify.ts +79 -0
  265. package/src/triggers/shortcut.ts +25 -0
  266. package/src/triggers/slack.ts +51 -0
  267. package/src/triggers/stripe.ts +70 -0
  268. package/src/triggers/teams.ts +67 -0
  269. package/src/triggers/telegram.ts +82 -0
  270. package/src/triggers/zendesk.ts +34 -0
  271. package/src/triggers.ts +7 -2
@@ -1,3 +1,4 @@
1
+ import type { ScheduleTriggerSource } from "./schedule.js";
1
2
  export type WebhookValue = null | boolean | number | string | readonly WebhookValue[] | {
2
3
  readonly [key: string]: WebhookValue;
3
4
  };
@@ -5,11 +6,13 @@ export type WebhookValue = null | boolean | number | string | readonly WebhookVa
5
6
  export type WebhookFilter = {
6
7
  readonly [key: string]: WebhookValue;
7
8
  };
8
- export interface TriggerSource {
9
+ export interface WebhookTriggerSource {
9
10
  readonly kind: "webhook";
10
11
  readonly name: string;
11
12
  readonly filter?: WebhookFilter;
12
13
  }
14
+ /** A webhook inbox subscription, or a `schedule.*` tick subscription. */
15
+ export type TriggerSource = WebhookTriggerSource | ScheduleTriggerSource;
13
16
  /** Plain, immutable data. Constructing a source opens no receiver. */
14
- export declare function webhook(name: string, filter?: WebhookFilter): TriggerSource;
17
+ export declare function webhook(name: string, filter?: WebhookFilter): WebhookTriggerSource;
15
18
  //# sourceMappingURL=triggers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"triggers.d.ts","sourceRoot":"","sources":["../src/triggers.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GACvD,SAAS,YAAY,EAAE,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAEvE,sEAAsE;AACtE,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAErE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;CACjC;AAED,sEAAsE;AACtE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,aAAa,CAS3E"}
1
+ {"version":3,"file":"triggers.d.ts","sourceRoot":"","sources":["../src/triggers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE3D,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GACvD,SAAS,YAAY,EAAE,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAEvE,sEAAsE;AACtE,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,CAAC;AAErE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;CACjC;AAED,yEAAyE;AACzE,MAAM,MAAM,aAAa,GAAG,oBAAoB,GAAG,qBAAqB,CAAC;AAEzE,sEAAsE;AACtE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,oBAAoB,CASlF"}
@@ -1 +1 @@
1
- {"version":3,"file":"triggers.js","sourceRoot":"","sources":["../src/triggers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"triggers.js","sourceRoot":"","sources":["../src/triggers.ts"],"names":[],"mappings":"AAiBA,sEAAsE;AACtE,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,MAAsB;IAC1D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChF,MAAM,IAAI,SAAS,CAAC,qGAAqG,CAAC,CAAC;IAC7H,CAAC;IACD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAkB,EAAE,CAAC,CAAC;AAC7F,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc,EAAE,YAAY,IAAI,GAAG,EAAU;IAC7D,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC5F,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;QACzG,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IACxE,CAAC;IACD,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrB,MAAM,OAAO,GAA6B,EAAE,CAAC;IAC7C,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,IAAI,KAAK,IAAI,GAAG,KAAK,QAAQ;YAAE,SAAS;QACxC,MAAM,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAE,CAAC;QAChE,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,UAAU,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,IAAI,UAAU,CAAC,EAAE,CAAC;YAClF,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,KAAK,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,6BAA6B,CAAC,CAAC;QAChG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,KAAK,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IAC9G,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AAC9F,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relayflows/surface",
3
- "version": "2.0.16",
3
+ "version": "2.0.17",
4
4
  "description": "TypeScript authoring surface for Relayflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/context.ts CHANGED
@@ -6,12 +6,30 @@ import type { Step } from "./step.js";
6
6
 
7
7
  export interface AgentResult {
8
8
  summary: string;
9
+ /**
10
+ * Files the agent created or changed under its working directory,
11
+ * cwd-relative POSIX paths, sorted — as journaled by the worker that spawned
12
+ * the CLI on the step's `step.completed`, never re-measured later. Empty
13
+ * for the relay transport (the agent ran elsewhere) and for an agent whose
14
+ * final message is a JSON object (that object is the output, unmodified).
15
+ */
9
16
  artifacts: string[];
10
17
  }
11
18
 
19
+ /** Per-step declarations: validated and recorded, not currently enforced (gate 8 / #442).
20
+ * Separate from flow-wide FlowHeader.workspace / tools.fs scopes.
21
+ */
22
+ export interface PermissionsSpec {
23
+ fileGlobs?: string[];
24
+ networkAllowlist?: string[];
25
+ accessPreset?: 'readonly' | 'readwrite';
26
+ }
27
+
12
28
  export interface AgentOptions {
13
29
  task: string;
14
30
  workspace?: string;
31
+ /** Validated declaration only; not currently enforced (gate 8 / #442). */
32
+ permissions?: PermissionsSpec;
15
33
  cli?: string;
16
34
  model?: string;
17
35
  /** Working directory for the CLI subprocess; defaults to the flow-runner's cwd. */
package/src/flow.ts CHANGED
@@ -3,6 +3,7 @@ import type { Helpers } from "./helpers/index.js";
3
3
  type HelperTools = Partial<Record<keyof Helpers, boolean>>;
4
4
  import type { Ctx } from "./context.js";
5
5
  import { webhook, type TriggerSource } from "./triggers.js";
6
+ import { schedule } from "./schedule.js";
6
7
 
7
8
  /** Optional escalation header; the empty header is the common case. */
8
9
  export interface FlowHeader {
@@ -91,9 +92,22 @@ function makeHandle(definition: AuthoredFlowDefinition): FlowHandle {
91
92
  value: <Event>(trigger: TriggerSource, body: FlowBody<Event>): TriggeredFlowHandle => {
92
93
  if (typeof body !== "function") throw new TypeError("trigger handler requires a body");
93
94
  assertHeaderObject(trigger, "trigger");
94
- assertKnownKeys(trigger, ["kind", "name", "filter"], "trigger");
95
- if (trigger.kind !== "webhook") throw new TypeError("unsupported trigger kind");
96
- const source = webhook(trigger.name, trigger.filter);
95
+ let source: TriggerSource;
96
+ if (trigger.kind === "schedule") {
97
+ assertKnownKeys(trigger, ["kind", "name", "cron", "tz", "intervalMs", "epochMs"], "trigger");
98
+ // Re-derive from the declaration rather than trusting the object: the
99
+ // parsed form is what the SDK lowers, so it must be one this module made.
100
+ source = trigger.cron !== undefined
101
+ ? schedule.cron(trigger.cron, trigger.tz === undefined ? {} : { tz: trigger.tz })
102
+ : schedule.every(`${Math.round((trigger.intervalMs ?? 0) / 1000)}s`);
103
+ if (source.intervalMs !== trigger.intervalMs || source.epochMs !== trigger.epochMs) {
104
+ throw new TypeError("schedule trigger grid does not match its declaration");
105
+ }
106
+ } else {
107
+ assertKnownKeys(trigger, ["kind", "name", "filter"], "trigger");
108
+ if (trigger.kind !== "webhook") throw new TypeError("unsupported trigger kind");
109
+ source = webhook(trigger.name, trigger.filter);
110
+ }
97
111
  return makeHandle(Object.freeze({
98
112
  ...definition,
99
113
  handlers: Object.freeze([...definition.handlers, Object.freeze({ trigger: source, body: body as FlowBody })]),
package/src/index.ts CHANGED
@@ -7,7 +7,7 @@ export type {
7
7
  WorkerSummary,
8
8
  CloudHelper,
9
9
  } from "./cloud.js";
10
- export type { AgentOptions, AgentResult, LlmOptions, Ctx } from "./context.js";
10
+ export type { AgentOptions, AgentResult, PermissionsSpec, LlmOptions, Ctx } from "./context.js";
11
11
  export {
12
12
  COMPLETION_REASONS,
13
13
  RUN_COMPLETION_REASONS,
@@ -41,7 +41,11 @@ export {
41
41
  } from "./slack.js";
42
42
  export type { Helpers } from "./helpers/index.js";
43
43
  export type { MemoryHelper, MemoryFinding, MemoryRecallOptions, HistoryEntry, TrajectoryEntry } from "./memory.js";
44
- export { webhook, type TriggerSource, type WebhookFilter, type WebhookValue } from "./triggers.js";
44
+ export { webhook, type TriggerSource, type WebhookTriggerSource, type WebhookFilter, type WebhookValue } from "./triggers.js";
45
+ export {
46
+ schedule, scheduleIdFor, parseCron, cronGrid, cronFixedIntervalMs, cronMaxGapMs, everyToMs,
47
+ type ScheduleTriggerSource, type ScheduleCronOptions, type CronFields, type CronGrid,
48
+ } from "./schedule.js";
45
49
  export * from "./triggers/index.js";
46
50
  export type { ProviderTriggerSource } from "./provider-trigger.js";
47
51
  export type { PluginMethod, PluginPrimitive } from './plugin-contract.js';
@@ -1,7 +1,7 @@
1
- import { webhook, type TriggerSource, type WebhookFilter } from "./triggers.js";
1
+ import { webhook, type WebhookTriggerSource, type WebhookFilter } from "./triggers.js";
2
2
 
3
3
  /** A provider inbox subscription over an envelope's type and payload. */
4
- export interface ProviderTriggerSource<Provider extends string, Event extends string> extends TriggerSource {
4
+ export interface ProviderTriggerSource<Provider extends string, Event extends string> extends WebhookTriggerSource {
5
5
  readonly name: Provider;
6
6
  readonly filter: WebhookFilter & { readonly provider: Provider; readonly type: Event };
7
7
  }
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Schedule triggers. Hand-written, unlike the provider namespaces under
3
+ * `triggers/`, because a schedule is not a provider event: there is no inbox
4
+ * and no upstream mapping to generate from. A schedule is an event source —
5
+ * locally the `flows tick` runner, on Cloud a relaycron cron — that submits
6
+ * `flows.tick` events, and this namespace declares which ticks a flow wants.
7
+ *
8
+ * Constructing a source opens nothing. The SDK lowers it to the same
9
+ * `flows.tick` subscription `testdata/tick-heartbeat.flow.yaml` writes by
10
+ * hand (`scheduleTriggerSpec`), and `flows schedule` sends the cron to Cloud.
11
+ */
12
+
13
+ export interface ScheduleTriggerSource {
14
+ readonly kind: "schedule";
15
+ /** Fixed: every schedule is delivered as a `flows.tick` event. */
16
+ readonly name: "flows.tick";
17
+ /** A 5-field cron expression, when declared with `schedule.cron`. */
18
+ readonly cron?: string;
19
+ /** IANA zone for `cron`; defaults to UTC. */
20
+ readonly tz?: string;
21
+ /** Fixed interval in milliseconds, when declared with `schedule.every` or when `cron` is exactly a tick grid. */
22
+ readonly intervalMs?: number;
23
+ /** Phase of that grid: the first fire is `epochMs` past the Unix epoch; present exactly when `intervalMs` is. */
24
+ readonly epochMs?: number;
25
+ }
26
+
27
+ export interface ScheduleCronOptions {
28
+ readonly tz?: string;
29
+ }
30
+
31
+ const EVERY = /^(\d+)(s|m|h|d)$/u;
32
+ const UNIT_MS = { s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 } as const;
33
+
34
+ /** `every("5m")` — a fixed interval. Units: s, m, h, d. Minimum one second. */
35
+ export function everyToMs(value: string): number {
36
+ const match = typeof value === "string" ? EVERY.exec(value.trim()) : null;
37
+ if (!match) throw new TypeError(`schedule.every expects <n><s|m|h|d>, got ${JSON.stringify(value)}`);
38
+ const ms = Number(match[1]) * UNIT_MS[match[2] as keyof typeof UNIT_MS];
39
+ if (!Number.isSafeInteger(ms) || ms < 1_000) throw new TypeError("schedule.every must be at least 1s");
40
+ return ms;
41
+ }
42
+
43
+ export interface CronFields {
44
+ readonly minute: string;
45
+ readonly hour: string;
46
+ readonly dayOfMonth: string;
47
+ readonly month: string;
48
+ readonly dayOfWeek: string;
49
+ }
50
+
51
+ const FIELD_RANGES = [[0, 59], [0, 23], [1, 31], [1, 12], [0, 7]] as const;
52
+ const NAMES: Record<number, readonly string[]> = {
53
+ 3: ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"],
54
+ 4: ["sun", "mon", "tue", "wed", "thu", "fri", "sat"],
55
+ };
56
+
57
+ /**
58
+ * Standard five-field cron. Accepts `*`, values, ranges, lists and `/step`,
59
+ * plus month and weekday names; refuses anything outside each field's range.
60
+ * Validation, not evaluation — the schedule's clock is the runner's.
61
+ */
62
+ export function parseCron(expression: string): CronFields {
63
+ const fields = typeof expression === "string" ? expression.trim().split(/\s+/u) : [];
64
+ if (fields.length !== 5) {
65
+ throw new TypeError(`cron expression must have five fields (minute hour day-of-month month day-of-week), got ${JSON.stringify(expression)}`);
66
+ }
67
+ fields.forEach((field, index) => {
68
+ const [low, high] = FIELD_RANGES[index]!;
69
+ for (const part of field.split(",")) {
70
+ const pieces = part.split("/");
71
+ if (pieces.length > 2 || part.length === 0) throw new TypeError(`cron field ${index + 1}: invalid ${JSON.stringify(part)}`);
72
+ const [range, step] = pieces;
73
+ if (step !== undefined && (!/^\d+$/u.test(step) || Number(step) < 1)) {
74
+ throw new TypeError(`cron field ${index + 1}: invalid step in ${JSON.stringify(part)}`);
75
+ }
76
+ if (range === "*") continue;
77
+ const bounds = range!.split("-");
78
+ if (bounds.length > 2) throw new TypeError(`cron field ${index + 1}: invalid range ${JSON.stringify(part)}`);
79
+ for (const bound of bounds) {
80
+ // Month names are 1-based (jan = 1); weekday names are 0-based (sun = 0).
81
+ const named = NAMES[index]?.indexOf(bound.toLowerCase());
82
+ const value = /^\d+$/u.test(bound) ? Number(bound)
83
+ : named === undefined || named === -1 ? undefined : named + (index === 3 ? 1 : 0);
84
+ if (value === undefined || value < low || value > high) {
85
+ throw new TypeError(`cron field ${index + 1}: ${JSON.stringify(bound)} is outside ${low}-${high}`);
86
+ }
87
+ }
88
+ }
89
+ });
90
+ return Object.freeze({
91
+ minute: fields[0]!, hour: fields[1]!, dayOfMonth: fields[2]!, month: fields[3]!, dayOfWeek: fields[4]!,
92
+ });
93
+ }
94
+
95
+ /** A tick grid that reproduces a cron exactly: fire every `intervalMs`, phase-aligned at `epochMs` past the Unix epoch. */
96
+ export interface CronGrid {
97
+ readonly intervalMs: number;
98
+ readonly epochMs: number;
99
+ }
100
+
101
+ // The tick grid a cron expression amounts to, when one reproduces it exactly.
102
+ // The tick source fires at epochMs + k*intervalMs, so a cron is representable
103
+ // only when its period divides the next field's cycle (every N minutes with
104
+ // 60 % N == 0, every N hours with 24 % N == 0) and its phase is a single
105
+ // fixed value in the finer fields. `*/7 * * * *` is not (cron restarts the
106
+ // minute count each hour, a grid does not); `15 * * * *` is, with a 15-minute
107
+ // phase. Anything with day, month or weekday constraints, lists or ranges is
108
+ // not a grid — only a cron-aware runner (Cloud's relaycron) can drive it.
109
+ // Time zones: a grid is anchored in UTC, so only UTC schedules qualify; an
110
+ // offset zone would shift the phase and a DST zone would move it twice a year.
111
+ export function cronGrid(fields: CronFields, tz?: string): CronGrid | undefined {
112
+ const { minute, hour, dayOfMonth, month, dayOfWeek } = fields;
113
+ if (dayOfMonth !== "*" || month !== "*" || dayOfWeek !== "*") return undefined;
114
+ if (tz !== undefined && tz !== "UTC" && tz !== "Etc/UTC") return undefined;
115
+ const every = (field: string, cycle: number): number | undefined => {
116
+ if (field === "*") return 1;
117
+ const match = /^\*\/(\d+)$/u.exec(field);
118
+ if (!match) return undefined;
119
+ const step = Number(match[1]);
120
+ return step >= 1 && cycle % step === 0 ? step : undefined;
121
+ };
122
+ const fixed = (field: string): number | undefined => (/^\d+$/u.test(field) ? Number(field) : undefined);
123
+ const minuteStep = every(minute, 60);
124
+ if (minuteStep !== undefined && hour === "*") return { intervalMs: minuteStep * UNIT_MS.m, epochMs: 0 };
125
+ const minutePhase = fixed(minute);
126
+ const hourStep = every(hour, 24);
127
+ if (minutePhase !== undefined && hourStep !== undefined) {
128
+ return { intervalMs: hourStep * UNIT_MS.h, epochMs: minutePhase * UNIT_MS.m };
129
+ }
130
+ const hourPhase = fixed(hour);
131
+ if (minutePhase !== undefined && hourPhase !== undefined) {
132
+ return { intervalMs: UNIT_MS.d, epochMs: hourPhase * UNIT_MS.h + minutePhase * UNIT_MS.m };
133
+ }
134
+ return undefined;
135
+ }
136
+
137
+ /** The fixed interval of a cron, when `cronGrid` finds one. Kept for callers that only need the period. */
138
+ export function cronFixedIntervalMs(fields: CronFields, tz?: string): number | undefined {
139
+ return cronGrid(fields, tz)?.intervalMs;
140
+ }
141
+
142
+ function cronFieldMatches(field: string, value: number, index: number): boolean {
143
+ const [low, high] = FIELD_RANGES[index]!;
144
+ const named = (bound: string): number => {
145
+ if (/^\d+$/u.test(bound)) return Number(bound);
146
+ return NAMES[index]!.indexOf(bound.toLowerCase()) + (index === 3 ? 1 : 0);
147
+ };
148
+ return field.split(",").some((part) => {
149
+ const [range, step] = part.split("/");
150
+ const stride = step === undefined ? 1 : Number(step);
151
+ let from: number;
152
+ let to: number;
153
+ // `N/step` runs from N to the field's maximum — for weekdays that is 7,
154
+ // so `1/2` reaches Sunday-as-7 and the normalisation below applies.
155
+ if (range === "*") { from = low; to = index === 4 ? 6 : high; }
156
+ else {
157
+ const bounds = range!.split("-").map(named);
158
+ from = bounds[0]!;
159
+ to = bounds.length === 2 ? bounds[1]! : step === undefined ? bounds[0]! : high;
160
+ }
161
+ // Weekday 7 is Sunday, as 0 is. A lone `7` or a range ending in 7 is
162
+ // tested against Sunday-as-0 as well, so a stride counted from the
163
+ // range start still lands on it: `1-7/2` fires Mon, Wed, Fri, Sun.
164
+ if (index === 4 && to === 7) {
165
+ if (from === 7) return value === 0;
166
+ if (value === 0 && (7 - from) % stride === 0) return true;
167
+ to = 6;
168
+ }
169
+ return value >= from && value <= to && (value - from) % stride === 0;
170
+ });
171
+ }
172
+
173
+ /**
174
+ * The longest gap between consecutive fires over two calendar years (one of
175
+ * them leap), in milliseconds — a cron's natural quiet period. Evaluated on
176
+ * the UTC calendar minute by minute; a zone offset does not change gap
177
+ * lengths, and a DST shift changes one gap by at most an hour, which the
178
+ * callers' multiple absorbs. An expression that fires at most once in the
179
+ * span reports the whole span. Returns undefined when it never fires.
180
+ */
181
+ export function cronMaxGapMs(fields: CronFields): number | undefined {
182
+ const start = Date.UTC(2024, 0, 1);
183
+ const end = Date.UTC(2026, 0, 1);
184
+ let previous: number | undefined;
185
+ let first: number | undefined;
186
+ let maxGap = 0;
187
+ for (let t = start; t < end; t += UNIT_MS.m) {
188
+ const d = new Date(t);
189
+ const dom = fields.dayOfMonth === "*";
190
+ const dow = fields.dayOfWeek === "*";
191
+ const dayMatches = dom && dow
192
+ ? true
193
+ // POSIX: when both day fields are restricted, either matching fires.
194
+ : dom !== dow
195
+ ? (dom ? cronFieldMatches(fields.dayOfWeek, d.getUTCDay(), 4) : cronFieldMatches(fields.dayOfMonth, d.getUTCDate(), 2))
196
+ : cronFieldMatches(fields.dayOfMonth, d.getUTCDate(), 2) || cronFieldMatches(fields.dayOfWeek, d.getUTCDay(), 4);
197
+ if (!dayMatches || !cronFieldMatches(fields.month, d.getUTCMonth() + 1, 3)
198
+ || !cronFieldMatches(fields.hour, d.getUTCHours(), 1) || !cronFieldMatches(fields.minute, d.getUTCMinutes(), 0)) continue;
199
+ if (previous !== undefined) maxGap = Math.max(maxGap, t - previous);
200
+ else first = t;
201
+ previous = t;
202
+ }
203
+ if (first === undefined || previous === undefined) return undefined;
204
+ return maxGap > 0 ? maxGap : end - start;
205
+ }
206
+
207
+ function assertTimeZone(tz: string): string {
208
+ if (typeof tz !== "string" || tz.trim().length === 0) throw new TypeError("schedule tz must be an IANA zone name");
209
+ try {
210
+ new Intl.DateTimeFormat("en-US", { timeZone: tz });
211
+ } catch {
212
+ throw new TypeError(`schedule tz ${JSON.stringify(tz)} is not a known IANA zone`);
213
+ }
214
+ return tz;
215
+ }
216
+
217
+ export const schedule = Object.freeze({
218
+ /** A five-field cron, evaluated in `tz` (default UTC). */
219
+ cron(expression: string, options: ScheduleCronOptions = {}): ScheduleTriggerSource {
220
+ const fields = parseCron(expression);
221
+ const tz = options.tz === undefined ? undefined : assertTimeZone(options.tz);
222
+ const grid = cronGrid(fields, tz);
223
+ return Object.freeze({
224
+ kind: "schedule", name: "flows.tick",
225
+ cron: expression.trim().split(/\s+/u).join(" "),
226
+ ...(tz === undefined ? {} : { tz }),
227
+ ...(grid === undefined ? {} : { intervalMs: grid.intervalMs, epochMs: grid.epochMs }),
228
+ });
229
+ },
230
+ /** A fixed interval: `every("5m")`, `every("2h")`. Units s, m, h, d. */
231
+ every(interval: string): ScheduleTriggerSource {
232
+ return Object.freeze({ kind: "schedule", name: "flows.tick", intervalMs: everyToMs(interval), epochMs: 0 });
233
+ },
234
+ });
235
+
236
+ /** UTF-8 code units of a string, without TextEncoder: this package compiles against no DOM or Node lib. */
237
+ function utf8Bytes(text: string): number[] {
238
+ const bytes: number[] = [];
239
+ for (const char of text) {
240
+ const cp = char.codePointAt(0)!;
241
+ if (cp < 0x80) bytes.push(cp);
242
+ else if (cp < 0x800) bytes.push(0xc0 | (cp >> 6), 0x80 | (cp & 0x3f));
243
+ else if (cp < 0x10000) bytes.push(0xe0 | (cp >> 12), 0x80 | ((cp >> 6) & 0x3f), 0x80 | (cp & 0x3f));
244
+ else bytes.push(0xf0 | (cp >> 18), 0x80 | ((cp >> 12) & 0x3f), 0x80 | ((cp >> 6) & 0x3f), 0x80 | (cp & 0x3f));
245
+ }
246
+ return bytes;
247
+ }
248
+
249
+ /** 64-bit FNV-1a over UTF-8, as 16 hex characters. Dependency-free; not a security hash, an identity. */
250
+ function fnv1a64(text: string): string {
251
+ let hi = 0xcbf29ce4, lo = 0x84222325;
252
+ for (const byte of utf8Bytes(text)) {
253
+ lo ^= byte;
254
+ // (hi:lo) * 0x100000001b3 mod 2^64, in 16-bit limbs to stay exact in doubles.
255
+ const l0 = lo & 0xffff, l1 = lo >>> 16, h0 = hi & 0xffff, h1 = hi >>> 16;
256
+ let a = l0 * 0x01b3;
257
+ let b = l1 * 0x01b3 + (a >>> 16);
258
+ let c = h0 * 0x01b3 + (b >>> 16) + l0;
259
+ let d = h1 * 0x01b3 + (c >>> 16) + l1;
260
+ lo = ((b & 0xffff) << 16 | (a & 0xffff)) >>> 0;
261
+ hi = ((d & 0xffff) << 16 | (c & 0xffff)) >>> 0;
262
+ }
263
+ return hi.toString(16).padStart(8, "0") + lo.toString(16).padStart(8, "0");
264
+ }
265
+
266
+ /**
267
+ * The `schedule_id` a flow's schedule subscribes to. Deterministic from the
268
+ * flow name and the declaration, so the local runner, `flows check` and the
269
+ * journal all name the same slot stream without a registry — and injective:
270
+ * a readable slug for humans, plus a hash of the exact declaration so
271
+ * `1-5` and `1,5`, or two zones, or two long names sharing a prefix, never
272
+ * share a stream (the kernel dispatches an event to its first matching
273
+ * subscription only).
274
+ */
275
+ export function scheduleIdFor(flowName: string, source: ScheduleTriggerSource): string {
276
+ const canonical = JSON.stringify([flowName, source.cron ?? null, source.tz ?? null, source.intervalMs ?? null, source.epochMs ?? null]);
277
+ const spec = source.cron !== undefined
278
+ ? `cron-${source.cron}${source.tz === undefined ? "" : `-${source.tz}`}`
279
+ : `every-${source.intervalMs}ms`;
280
+ const slug = `${flowName}-${spec}`.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-|-$/gu, "").slice(0, 96);
281
+ return `${slug}-${fnv1a64(canonical)}`;
282
+ }
package/src/step.ts CHANGED
@@ -8,11 +8,14 @@ export interface Step<T> extends PromiseLike<T> {
8
8
  */
9
9
  gate(config: NamedGate): Step<T>;
10
10
  /**
11
- * Predicate gates cannot be journaled the JavaScript closure would not
12
- * survive replay so the executor refuses this branch with
13
- * `unsupported_gate`. Use a `NamedGate` config-object gate above, or the
14
- * declarative `verification:` field on the compiled step spec, when you
15
- * need journal-honest verification.
11
+ * Predicate gate: author code, run once by the runtime after the step
12
+ * completes, on the value the journal handed back. The closure itself is
13
+ * never serialized; its VERDICT is journaled as a lowered `<step>.gate`
14
+ * deterministic step that succeeds or fails, so resume and replay read the
15
+ * recorded verdict and never re-run the function. A false verdict fails the
16
+ * run as `gate_failed`, carrying `because`. `flows check` cannot prove a
17
+ * predicate (docs/SURFACE.md §6); use a `NamedGate` when the check must be
18
+ * inspectable before execution. One `.gate()` per step.
16
19
  */
17
20
  gate(predicate: (value: T) => boolean, because?: string): Step<T>;
18
21
  }
@@ -27,7 +30,20 @@ export type NamedGate =
27
30
  | ReferencesInputNamedGate
28
31
  | SubprocessNamedGate
29
32
  | WordCountBoundsNamedGate
30
- | RegexMatchNamedGate;
33
+ | RegexMatchNamedGate
34
+ | ArtifactExistsNamedGate;
35
+
36
+ /**
37
+ * Passes when the step's journaled `artifacts` lists `path` — a file the
38
+ * agent's worker measured as created or changed under its working directory.
39
+ * The check reads the journal, so replay and resume see the verdict that was
40
+ * recorded, never a fresh look at the disk.
41
+ */
42
+ export interface ArtifactExistsNamedGate {
43
+ type: 'artifact_exists';
44
+ /** Working-directory-relative POSIX path, e.g. `review/security.md`. */
45
+ path: string;
46
+ }
31
47
 
32
48
  export interface ReferencesInputNamedGate {
33
49
  type: 'references_input';
@@ -0,0 +1,56 @@
1
+ <!-- GENERATED by scripts/generate-triggers.mjs — do not edit. -->
2
+ # Provider trigger namespaces
3
+
4
+ 47 providers, 570 events. `mapping` rows come from the adapter's `webhooks:` block (payload-aware signatures); `catalog` rows come from `KNOWN_TRIGGER_CATALOG` (`supportedEvents()`) only. Every provider also includes its catalog events, so the count is what ingress delivers.
5
+
6
+ | Provider | Namespace | Events | Source | Registry-only events |
7
+ |---|---|---:|---|---|
8
+ | `airtable` | `airtable` | 9 | catalog | |
9
+ | `asana` | `asana` | 17 | catalog | |
10
+ | `azure-blob` | `azure_blob` | 3 | mapping | |
11
+ | `box` | `box` | 3 | mapping | |
12
+ | `calendly` | `calendly` | 12 | catalog | |
13
+ | `clickup` | `clickup` | 12 | catalog | |
14
+ | `cloudflare` | `cloudflare` | 8 | mapping | |
15
+ | `confluence` | `confluence` | 6 | catalog | |
16
+ | `daytona` | `daytona` | 7 | mapping | |
17
+ | `docker-hub` | `docker_hub` | 1 | catalog | |
18
+ | `dropbox` | `dropbox` | 1 | mapping | |
19
+ | `fathom` | `fathom` | 1 | mapping | |
20
+ | `gcp` | `gcp` | 9 | mapping | |
21
+ | `gcs` | `gcs` | 3 | mapping | |
22
+ | `github` | `github` | 26 | mapping | |
23
+ | `gitlab` | `gitlab` | 53 | mapping | |
24
+ | `gmail` | `gmail` | 3 | mapping | |
25
+ | `google-calendar` | `google_calendar` | 3 | catalog | |
26
+ | `google-drive` | `google_drive` | 3 | mapping | |
27
+ | `granola` | `granola` | 4 | mapping | |
28
+ | `hubspot` | `hubspot` | 20 | catalog | |
29
+ | `intercom` | `intercom` | 18 | catalog | |
30
+ | `jira` | `jira` | 12 | catalog | |
31
+ | `linear` | `linear` | 33 | catalog | |
32
+ | `mailgun` | `mailgun` | 15 | catalog | |
33
+ | `mixpanel` | `mixpanel` | 12 | catalog | |
34
+ | `neon` | `neon` | 5 | mapping | |
35
+ | `notion` | `notion` | 13 | mapping | |
36
+ | `onedrive` | `onedrive` | 3 | mapping | |
37
+ | `pipedrive` | `pipedrive` | 14 | catalog | |
38
+ | `postgres` | `postgres` | 3 | mapping | |
39
+ | `posthog` | `posthog` | 2 | mapping | |
40
+ | `ramp` | `ramp` | 47 | mapping | |
41
+ | `recall` | `recall` | 3 | mapping | |
42
+ | `redis` | `redis` | 3 | mapping | |
43
+ | `s3` | `s3` | 3 | mapping | |
44
+ | `salesforce` | `salesforce` | 22 | catalog | |
45
+ | `segment` | `segment` | 12 | catalog | |
46
+ | `sendgrid` | `sendgrid` | 17 | catalog | |
47
+ | `sharepoint` | `sharepoint` | 3 | mapping | |
48
+ | `shopify` | `shopify` | 24 | catalog | |
49
+ | `shortcut` | `shortcut` | 6 | mapping | |
50
+ | `slack` | `slack` | 21 | mapping | `reaction.added` |
51
+ | `stripe` | `stripe` | 21 | catalog | |
52
+ | `teams` | `teams` | 20 | catalog | |
53
+ | `telegram` | `telegram` | 25 | catalog | |
54
+ | `zendesk` | `zendesk` | 9 | catalog | |
55
+
56
+ Registry-only events share an identifier with another event of the same provider; subscribe with `webhook(provider, { provider, type })`.
@@ -9,8 +9,25 @@ export default flow('triage')
9
9
  .on(github.pull_request('opened'), async (f, event) => { f.done('success'); });
10
10
  ```
11
11
 
12
+ Every provider a relayfile adapter can deliver has a namespace — 47 today,
13
+ 570 events; see [PROVIDERS.md](PROVIDERS.md) (generated) for the full table.
14
+ Hyphenated providers become identifiers: `azure-blob` → `azure_blob`,
15
+ `google-drive` → `google_drive`; the inbox name and event type keep the
16
+ upstream spelling, so `azure_blob.file_created()` lowers to
17
+ `{ provider: 'azure-blob', type: 'file.created' }`. Namespaces come from two
18
+ sources: an adapter's `webhooks:` mapping block, which carries payload shape
19
+ (`github.pull_request(action?)` takes an action because the mapping extracts
20
+ one), and the trigger catalog (`@relayfile/adapter-core/triggers`, every adapter's
21
+ `supportedEvents()`), which is the full set an adapter delivers and yields the
22
+ plain `(filter?)` signature. The two are unioned per provider: a mapping
23
+ covers payload shape for some events, never the whole set (gitlab maps 8 of
24
+ 53). Where two upstream names share an identifier (`reaction.added` and
25
+ `reaction_added`), the mapping-declared one owns the method and the other
26
+ stays subscribable via `webhook(provider, { provider, type })`; PROVIDERS.md
27
+ lists them.
28
+
12
29
  Provider namespaces also export from `@relayflows/surface/triggers` and
13
- `@relayflows/surface/triggers/slack` (or `/github`). Declarations are immutable
30
+ `@relayflows/surface/triggers/slack` (or `/github`, `/notion`, …). Declarations are immutable
14
31
  webhook sources: their executor/inbox name is the provider, and their filter
15
32
  matches the provider, event type, and requested payload fields. Register those
16
33
  provider names in `flows.json`'s `executors` array for preflight.
@@ -32,9 +49,21 @@ filter names that same provider, and the filter pins an event type.
32
49
  `slack.reaction(emoji)` subscribes to `reaction_added`. Arguments match provider
33
50
  values exactly: use the channel ID and reaction name from the incoming event.
34
51
  `github.pull_request(action)` filters `payload.action`; omitting the action
35
- accepts every pull request event. Upstream mappings do not declare action enums,
36
- so the action parameter is a string. Other generated methods accept an optional
37
- recursive payload filter, for example `github.push({ ref: 'refs/heads/main' })`.
52
+ accepts every pull request event. GitHub (and some other providers) appear in
53
+ two spellings because two ingresses deliver them: the aggregate event with the
54
+ action in the payload (`pull_request` + `payload.action`) is what a raw GitHub
55
+ delivery and this package's `POST /providers/github` receiver carry, while the
56
+ action-qualified name (`pull_request.opened`) is what relayfile's Cloud
57
+ ingress normalizes to and what the adapter catalog lists. Subscribe in the
58
+ spelling of the ingress that will deliver to you; action-qualified methods
59
+ take only a payload filter, never a second action. Upstream mappings do not declare action enums,
60
+ so the action parameter is a string. `github.check_run(action)` (a CI check
61
+ finished: `completed`, with `check_run.conclusion` in the payload) and
62
+ `github.issue_comment(action)` (a comment on an issue or pull-request
63
+ conversation: `created`, `edited`, `deleted`) take an action the same way — the
64
+ two events a PR reviewer needs for merge-on-green and comment-driven directives.
65
+ Other generated methods accept an optional recursive payload filter, for
66
+ example `github.push({ ref: 'refs/heads/main' })`.
38
67
 
39
68
  The receiver accepts `POST /providers/slack` with this JSON:
40
69
 
@@ -72,9 +101,55 @@ Each provider inbox still binds one RunSpec. This extends E's declaration and
72
101
  inbox contract; deploying TypeScript handler bodies remains the separate #301
73
102
  binding step.
74
103
 
104
+ ## Schedules
105
+
106
+ `schedule` is hand-written, not generated: a schedule is not a provider event.
107
+
108
+ ```ts
109
+ import { flow, schedule } from '@relayflows/surface';
110
+
111
+ export default flow('nightly')
112
+ .on(schedule.cron('0 9 * * 1-5', { tz: 'Europe/Oslo' }), async (f) => { f.done('success'); })
113
+ .on(schedule.every('5m'), async (f) => { f.done('success'); });
114
+ ```
115
+
116
+ `schedule.cron` validates a standard five-field expression (values, ranges,
117
+ lists, `/step`, month and weekday names) and an IANA `tz`; `schedule.every`
118
+ takes `<n><s|m|h|d>`, at least one second. Both are inert data. The SDK lowers
119
+ a declaration to the `flows.tick` subscription `testdata/tick-heartbeat.flow.yaml`
120
+ writes by hand — event type `flows.tick`, pattern `schedule_id`, the tick
121
+ dedupe key, and a three-slot silence budget — with a deterministic
122
+ `schedule_id` from the flow name and the declaration (`scheduleIdFor`).
123
+ `flows check` prints each one:
124
+
125
+ ```
126
+ SCHEDULE handler 0 every 300000ms -> flows.tick schedule_id nightly-every-300000ms-33a704f768702f7d [local: flows tick start --schedule-id nightly-every-300000ms-33a704f768702f7d --interval-ms 300000 --epoch-ms 0]
127
+ SCHEDULE handler 1 cron "0 9 * * 1-5" tz Europe/Oslo -> flows.tick schedule_id nightly-cron-0-9-1-5-europe-oslo-41f5a267fd758564 [Cloud only: ...]
128
+ ```
129
+
130
+ The id is a readable slug plus a hash of the exact declaration, so `1-5` and
131
+ `1,5`, two zones, or two long names never share a tick stream (the kernel
132
+ dispatches an event to its first matching subscription only).
133
+
134
+ Locally the tick runner fires at `epoch + k·interval`, so it drives a cron
135
+ only when that grid reproduces it **exactly**: `every(...)`; `*/N * * * *`
136
+ with `N` dividing 60; `M */N * * *` with `N` dividing 24 (phase `M`
137
+ minutes); `M H * * *` daily (phase `H:M`) — all in UTC. `*/7 * * * *` is not
138
+ a grid (cron restarts the count each hour, a grid drifts), nor is anything
139
+ with day, month or weekday constraints, lists, ranges, or a non-UTC zone.
140
+ Those are reported as Cloud-only rather than approximated; `flows schedule`
141
+ registers them on Cloud's cron-aware runner. Every schedule declares its own
142
+ silence budget — three intervals for a grid, three times the cron's longest
143
+ quiet period otherwise (`cronMaxGapMs`) — so a weekday-morning cron is not
144
+ journaled stale on Monday at 09:05 by the kernel's five-minute default.
145
+ Schedules need no `flows.json` executor entry: the tick source ships with
146
+ the CLI.
147
+
75
148
  Generation requires the SDK's development dependencies. The default input is
76
- the mapping YAML shipped in the pinned `@relayfile/adapter-core` dependency
77
- (currently Slack and GitHub). A checkout supplies additional providers:
149
+ the pinned `@relayfile/adapter-core` dependency: its `mappings/` fallbacks,
150
+ its `mappings/adapters/` bundle (every adapter's own mapping, adapter-core
151
+ ≥ 0.5.26), and its trigger catalog. A checkout supplies the same three from
152
+ source:
78
153
 
79
154
  ```sh
80
155
  node scripts/generate-triggers.mjs
@@ -83,6 +158,7 @@ node scripts/generate-triggers.mjs --adapters-dir /path/to/relayfile-adapters
83
158
  ```
84
159
 
85
160
  The generator reads `packages/core/mappings/*.mapping.yaml`, then each adapter's
86
- `packages/<adapter>/*.mapping.yaml`; adapter-local mappings take precedence.
87
- Only adapters with a nonempty `webhooks:` section produce modules. It never
161
+ `packages/<adapter>/*.mapping.yaml` (adapter-local mappings take precedence),
162
+ then `packages/core/src/triggers/catalog.generated.json` for providers with no
163
+ `webhooks:` block. Only providers with at least one event produce modules. It never
88
164
  imports or executes adapter code. Rebuild the surface and SDK after regeneration.