@greatwait/contracts 0.1.0-alpha.2 → 0.1.0-alpha.4

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 (349) hide show
  1. package/README.md +11 -9
  2. package/dist/authority.d.ts +84 -21
  3. package/dist/authority.js +47 -2
  4. package/dist/billing.d.ts +1001 -0
  5. package/dist/billing.js +684 -0
  6. package/dist/callback-safety.d.ts +2 -2
  7. package/dist/callback-safety.js +2 -2
  8. package/dist/compatibility.d.ts +3 -3
  9. package/dist/compatibility.js +1 -1
  10. package/dist/data-export.d.ts +67 -0
  11. package/dist/data-export.js +182 -0
  12. package/dist/endpoints.d.ts +181 -240
  13. package/dist/endpoints.js +77 -81
  14. package/dist/events.d.ts +17 -17
  15. package/dist/events.js +1 -1
  16. package/dist/first-value-journey.d.ts +192 -42
  17. package/dist/first-value-journey.js +212 -6
  18. package/dist/forecast.d.ts +71 -71
  19. package/dist/forecast.js +1 -1
  20. package/dist/identifiers.d.ts +20 -20
  21. package/dist/identifiers.js +1 -1
  22. package/dist/identity.d.ts +45 -41
  23. package/dist/identity.js +5 -4
  24. package/dist/index.d.ts +13 -8
  25. package/dist/index.js +13 -8
  26. package/dist/mcp.d.ts +201 -0
  27. package/dist/mcp.js +214 -0
  28. package/dist/naming.d.ts +45 -0
  29. package/dist/naming.js +159 -0
  30. package/dist/oauth.d.ts +186 -172
  31. package/dist/oauth.js +84 -8
  32. package/dist/operation-definitions.d.ts +9 -0
  33. package/dist/operation-definitions.js +7 -0
  34. package/dist/operations.d.ts +4359 -2214
  35. package/dist/operations.js +2754 -881
  36. package/dist/organization-lifecycle.d.ts +34 -0
  37. package/dist/organization-lifecycle.js +122 -0
  38. package/dist/partition.d.ts +26 -0
  39. package/dist/policy.d.ts +4 -4
  40. package/dist/policy.js +9 -5
  41. package/dist/problems.d.ts +32 -26
  42. package/dist/problems.js +6 -2
  43. package/dist/protocol.d.ts +204 -75
  44. package/dist/protocol.js +108 -36
  45. package/dist/scheduling-records.d.ts +343 -285
  46. package/dist/scheduling-records.js +1 -1
  47. package/dist/scheduling.d.ts +670 -360
  48. package/dist/scheduling.js +382 -56
  49. package/dist/secrets.d.ts +119 -105
  50. package/dist/secrets.js +1 -1
  51. package/dist/signatures.d.ts +74 -74
  52. package/dist/signatures.js +1 -1
  53. package/dist/tenancy.d.ts +142 -68
  54. package/dist/tenancy.js +70 -17
  55. package/dist/test-sink.d.ts +127 -127
  56. package/dist/test-sink.js +1 -1
  57. package/dist/time.d.ts +4 -4
  58. package/dist/time.js +1 -1
  59. package/dist/timing.d.ts +65 -0
  60. package/dist/timing.js +82 -0
  61. package/dist/usage.d.ts +103 -47
  62. package/dist/usage.js +197 -31
  63. package/dist/workspace.d.ts +34 -34
  64. package/dist/workspace.js +1 -1
  65. package/dist/zod-values.d.ts +3 -0
  66. package/dist/zod-values.js +2 -0
  67. package/dist/zod.d.ts +2 -0
  68. package/dist/zod.js +2 -0
  69. package/generated/v1/examples/attempts.list.response.json +4 -0
  70. package/generated/v1/examples/attempts.read.response.json +4 -0
  71. package/generated/v1/examples/billing.autotopup.request.json +5 -0
  72. package/generated/v1/examples/billing.autotopup.response.json +120 -0
  73. package/generated/v1/examples/billing.autotopuppreview.request.json +19 -0
  74. package/generated/v1/examples/billing.autotopuppreview.response.json +45 -0
  75. package/generated/v1/examples/billing.budgets.request.json +13 -0
  76. package/generated/v1/examples/billing.budgets.response.json +22 -0
  77. package/generated/v1/examples/billing.ceiling.request.json +8 -0
  78. package/generated/v1/examples/billing.ceiling.response.json +120 -0
  79. package/generated/v1/examples/billing.change.request.json +5 -0
  80. package/generated/v1/examples/billing.change.response.json +12 -0
  81. package/generated/v1/examples/billing.funding.response.json +120 -0
  82. package/generated/v1/examples/billing.fundingquote.request.json +7 -0
  83. package/generated/v1/examples/billing.fundingquote.response.json +13 -0
  84. package/generated/v1/examples/billing.ledger.response.json +23 -0
  85. package/generated/v1/examples/billing.notices.response.json +41 -0
  86. package/generated/v1/examples/billing.notificationsettings.response.json +17 -0
  87. package/generated/v1/examples/billing.notificationsettingsupdate.request.json +9 -0
  88. package/generated/v1/examples/billing.notificationsettingsupdate.response.json +17 -0
  89. package/generated/v1/examples/billing.portal.response.json +3 -0
  90. package/generated/v1/examples/billing.projects.response.json +12 -0
  91. package/generated/v1/examples/billing.purchase.request.json +5 -0
  92. package/generated/v1/examples/billing.purchase.response.json +120 -0
  93. package/generated/v1/examples/billing.quote.request.json +4 -0
  94. package/generated/v1/examples/billing.quote.response.json +19 -0
  95. package/generated/v1/examples/billing.reconcile.response.json +73 -0
  96. package/generated/v1/examples/billing.refundrequest.request.json +10 -0
  97. package/generated/v1/examples/billing.refundrequest.response.json +11 -0
  98. package/generated/v1/examples/billing.refundresolution.request.json +4 -0
  99. package/generated/v1/examples/billing.refundresolution.response.json +120 -0
  100. package/generated/v1/examples/billing.summary.response.json +73 -0
  101. package/generated/v1/examples/context.read.response.json +6 -0
  102. package/generated/v1/examples/credentials.connection.response.json +16 -0
  103. package/generated/v1/examples/endpoints.acknowledgeSigningKeyRotation.response.json +18 -0
  104. package/generated/v1/examples/endpoints.create.response.json +1 -1
  105. package/generated/v1/examples/endpoints.disable.response.json +0 -1
  106. package/generated/v1/examples/{endpoints.verify.response.json → endpoints.enable.response.json} +4 -5
  107. package/generated/v1/examples/endpoints.list.response.json +1 -2
  108. package/generated/v1/examples/endpoints.read.response.json +2 -2
  109. package/generated/v1/examples/endpoints.revealSigningSecret.response.json +7 -0
  110. package/generated/v1/examples/endpoints.rotateSigningKey.response.json +1 -2
  111. package/generated/v1/examples/endpoints.update.response.json +1 -1
  112. package/generated/v1/examples/executions.list.response.json +20 -0
  113. package/generated/v1/examples/exports.cancel.response.json +11 -0
  114. package/generated/v1/examples/exports.create.request.json +1 -0
  115. package/generated/v1/examples/exports.create.response.json +8 -0
  116. package/generated/v1/examples/exports.part.response.json +5 -0
  117. package/generated/v1/examples/exports.read.response.json +8 -0
  118. package/generated/v1/examples/occurrences.read.response.json +4 -0
  119. package/generated/v1/examples/organizations.close.request.json +5 -0
  120. package/generated/v1/examples/organizations.close.response.json +10 -0
  121. package/generated/v1/examples/organizations.lifecycle.response.json +10 -0
  122. package/generated/v1/examples/organizations.restore.request.json +3 -0
  123. package/generated/v1/examples/organizations.restore.response.json +10 -0
  124. package/generated/v1/examples/projects.archive.request.json +3 -0
  125. package/generated/v1/examples/projects.archive.response.json +14 -0
  126. package/generated/v1/examples/projects.create.response.json +10 -1
  127. package/generated/v1/examples/projects.delete.response.json +9 -0
  128. package/generated/v1/examples/projects.list.response.json +12 -2
  129. package/generated/v1/examples/projects.rename.request.json +3 -0
  130. package/generated/v1/examples/projects.rename.response.json +14 -0
  131. package/generated/v1/examples/schedules.archive.response.json +3 -0
  132. package/generated/v1/examples/schedules.cancel.response.json +3 -0
  133. package/generated/v1/examples/schedules.create.request.json +4 -0
  134. package/generated/v1/examples/schedules.create.response.json +3 -0
  135. package/generated/v1/examples/schedules.list.response.json +3 -0
  136. package/generated/v1/examples/schedules.pause.response.json +3 -0
  137. package/generated/v1/examples/schedules.read.response.json +6 -0
  138. package/generated/v1/examples/schedules.replace.request.json +4 -0
  139. package/generated/v1/examples/schedules.replace.response.json +3 -0
  140. package/generated/v1/examples/schedules.resume.response.json +3 -0
  141. package/generated/v1/examples/timing.summary.response.json +35 -0
  142. package/generated/v1/examples/usage.read.response.json +1 -0
  143. package/generated/v1/first-value-journey.json +19 -10
  144. package/generated/v1/manifest.json +1019 -53
  145. package/generated/v1/openapi.json +30597 -5709
  146. package/generated/v1/policy-matrix.json +290 -45
  147. package/generated/v1/schemas/attempts.list.response.json +24 -1
  148. package/generated/v1/schemas/attempts.read.response.json +24 -1
  149. package/generated/v1/schemas/billing.autotopup.request-headers.json +13 -0
  150. package/generated/v1/schemas/billing.autotopup.request.json +22 -0
  151. package/generated/v1/schemas/billing.autotopup.response-headers.json +10 -0
  152. package/generated/v1/schemas/billing.autotopup.response.json +552 -0
  153. package/generated/v1/schemas/billing.autotopuppreview.request.json +77 -0
  154. package/generated/v1/schemas/billing.autotopuppreview.response.json +277 -0
  155. package/generated/v1/schemas/billing.budgets.request-headers.json +13 -0
  156. package/generated/v1/schemas/billing.budgets.request.json +66 -0
  157. package/generated/v1/schemas/billing.budgets.response-headers.json +10 -0
  158. package/generated/v1/schemas/billing.budgets.response.json +92 -0
  159. package/generated/v1/schemas/billing.ceiling.request-headers.json +13 -0
  160. package/generated/v1/schemas/billing.ceiling.request.json +40 -0
  161. package/generated/v1/schemas/billing.ceiling.response-headers.json +10 -0
  162. package/generated/v1/schemas/billing.ceiling.response.json +552 -0
  163. package/generated/v1/schemas/billing.change.request-headers.json +13 -0
  164. package/generated/v1/schemas/billing.change.request.json +22 -0
  165. package/generated/v1/schemas/billing.change.response.json +119 -0
  166. package/generated/v1/schemas/billing.funding.response.json +552 -0
  167. package/generated/v1/schemas/billing.fundingquote.request.json +28 -0
  168. package/generated/v1/schemas/billing.fundingquote.response.json +61 -0
  169. package/generated/v1/schemas/billing.ledger.query.json +16 -0
  170. package/generated/v1/schemas/billing.ledger.response.json +125 -0
  171. package/generated/v1/schemas/billing.notices.query.json +16 -0
  172. package/generated/v1/schemas/billing.notices.response.json +278 -0
  173. package/generated/v1/schemas/billing.notificationsettings.response.json +70 -0
  174. package/generated/v1/schemas/billing.notificationsettingsupdate.request-headers.json +13 -0
  175. package/generated/v1/schemas/billing.notificationsettingsupdate.request.json +36 -0
  176. package/generated/v1/schemas/billing.notificationsettingsupdate.response-headers.json +10 -0
  177. package/generated/v1/schemas/billing.notificationsettingsupdate.response.json +70 -0
  178. package/generated/v1/schemas/billing.portal.response.json +11 -0
  179. package/generated/v1/schemas/billing.projects.response.json +119 -0
  180. package/generated/v1/schemas/billing.purchase.request-headers.json +13 -0
  181. package/generated/v1/schemas/billing.purchase.request.json +22 -0
  182. package/generated/v1/schemas/billing.purchase.response-headers.json +10 -0
  183. package/generated/v1/schemas/billing.purchase.response.json +552 -0
  184. package/generated/v1/schemas/billing.quote.request.json +20 -0
  185. package/generated/v1/schemas/billing.quote.response.json +86 -0
  186. package/generated/v1/schemas/billing.reconcile.response.json +468 -0
  187. package/generated/v1/schemas/billing.refundrequest.request-headers.json +13 -0
  188. package/generated/v1/schemas/billing.refundrequest.request.json +42 -0
  189. package/generated/v1/schemas/billing.refundrequest.response-headers.json +10 -0
  190. package/generated/v1/schemas/billing.refundrequest.response.json +45 -0
  191. package/generated/v1/schemas/billing.refundresolution.request-headers.json +13 -0
  192. package/generated/v1/schemas/billing.refundresolution.request.json +61 -0
  193. package/generated/v1/schemas/billing.refundresolution.response-headers.json +10 -0
  194. package/generated/v1/schemas/billing.refundresolution.response.json +552 -0
  195. package/generated/v1/schemas/billing.summary.response.json +468 -0
  196. package/generated/v1/schemas/context.read.response.json +45 -0
  197. package/generated/v1/schemas/credentials.connection.response.json +66 -0
  198. package/generated/v1/schemas/credentials.create.request.json +2 -0
  199. package/generated/v1/schemas/credentials.create.response.json +2 -0
  200. package/generated/v1/schemas/credentials.list.response.json +2 -0
  201. package/generated/v1/schemas/credentials.revoke.response.json +2 -0
  202. package/generated/v1/schemas/credentials.rotate.response.json +4 -0
  203. package/generated/v1/schemas/{endpoints.verify.request-headers.json → endpoints.acknowledgeSigningKeyRotation.request-headers.json} +2 -1
  204. package/generated/v1/schemas/{endpoints.verify.response.json → endpoints.acknowledgeSigningKeyRotation.response.json} +8 -21
  205. package/generated/v1/schemas/endpoints.create.request.json +5 -0
  206. package/generated/v1/schemas/endpoints.create.response.json +8 -21
  207. package/generated/v1/schemas/endpoints.disable.response.json +8 -21
  208. package/generated/v1/schemas/endpoints.enable.response-headers.json +11 -0
  209. package/generated/v1/schemas/endpoints.enable.response.json +87 -0
  210. package/generated/v1/schemas/endpoints.list.response.json +8 -21
  211. package/generated/v1/schemas/endpoints.read.response.json +8 -21
  212. package/generated/v1/schemas/endpoints.revealSigningSecret.response.json +43 -0
  213. package/generated/v1/schemas/endpoints.rotateSigningKey.response.json +8 -21
  214. package/generated/v1/schemas/endpoints.update.request.json +5 -0
  215. package/generated/v1/schemas/endpoints.update.response.json +8 -21
  216. package/generated/v1/schemas/executions.list.query.json +46 -0
  217. package/generated/v1/schemas/executions.list.response.json +116 -0
  218. package/generated/v1/schemas/exports.cancel.response.json +48 -0
  219. package/generated/v1/schemas/exports.create.request.json +6 -0
  220. package/generated/v1/schemas/exports.create.response.json +38 -0
  221. package/generated/v1/schemas/exports.part.query.json +11 -0
  222. package/generated/v1/schemas/exports.part.response.json +22 -0
  223. package/generated/v1/schemas/exports.read.response.json +38 -0
  224. package/generated/v1/schemas/grants.list.response.json +2 -0
  225. package/generated/v1/schemas/grants.revoke.response.json +2 -0
  226. package/generated/v1/schemas/occurrences.list.query.json +5 -0
  227. package/generated/v1/schemas/occurrences.list.response.json +23 -0
  228. package/generated/v1/schemas/occurrences.read.response.json +23 -0
  229. package/generated/v1/schemas/occurrences.redeliver.response.json +24 -1
  230. package/generated/v1/schemas/organizations.close.request.json +20 -0
  231. package/generated/v1/schemas/organizations.close.response.json +68 -0
  232. package/generated/v1/schemas/organizations.lifecycle.response.json +68 -0
  233. package/generated/v1/schemas/organizations.restore.request.json +13 -0
  234. package/generated/v1/schemas/organizations.restore.response.json +68 -0
  235. package/generated/v1/schemas/projects.archive.request-headers.json +11 -0
  236. package/generated/v1/schemas/projects.archive.request.json +10 -0
  237. package/generated/v1/schemas/projects.archive.response-headers.json +11 -0
  238. package/generated/v1/schemas/projects.archive.response.json +79 -0
  239. package/generated/v1/schemas/projects.create.response.json +55 -1
  240. package/generated/v1/schemas/projects.delete.request-headers.json +11 -0
  241. package/generated/v1/schemas/projects.delete.response.json +37 -0
  242. package/generated/v1/schemas/projects.list.response.json +61 -2
  243. package/generated/v1/schemas/projects.rename.request-headers.json +11 -0
  244. package/generated/v1/schemas/projects.rename.request.json +12 -0
  245. package/generated/v1/schemas/projects.rename.response-headers.json +11 -0
  246. package/generated/v1/schemas/projects.rename.response.json +79 -0
  247. package/generated/v1/schemas/schedules.archive.response.json +90 -2
  248. package/generated/v1/schemas/schedules.cancel.response.json +90 -2
  249. package/generated/v1/schemas/schedules.create.request.json +24 -3
  250. package/generated/v1/schemas/schedules.create.response.json +90 -2
  251. package/generated/v1/schemas/schedules.list.query.json +13 -0
  252. package/generated/v1/schemas/schedules.list.response.json +67 -2
  253. package/generated/v1/schemas/schedules.pause.response.json +90 -2
  254. package/generated/v1/schemas/schedules.read.response.json +90 -2
  255. package/generated/v1/schemas/schedules.replace.request.json +24 -3
  256. package/generated/v1/schemas/schedules.replace.response.json +90 -2
  257. package/generated/v1/schemas/schedules.resume.response.json +90 -2
  258. package/generated/v1/schemas/timing.summary.response.json +191 -0
  259. package/generated/v1/schemas/usage.read.query.json +7 -0
  260. package/generated/v1/schemas/usage.read.response.json +36 -1
  261. package/generated/v1/snippets/attempts.list.json +1 -0
  262. package/generated/v1/snippets/attempts.read.json +1 -0
  263. package/generated/v1/snippets/billing.autotopup.json +6 -0
  264. package/generated/v1/snippets/billing.autotopuppreview.json +6 -0
  265. package/generated/v1/snippets/billing.budgets.json +6 -0
  266. package/generated/v1/snippets/billing.ceiling.json +6 -0
  267. package/generated/v1/snippets/billing.change.json +6 -0
  268. package/generated/v1/snippets/billing.funding.json +6 -0
  269. package/generated/v1/snippets/billing.fundingquote.json +6 -0
  270. package/generated/v1/snippets/billing.ledger.json +6 -0
  271. package/generated/v1/snippets/billing.notices.json +6 -0
  272. package/generated/v1/snippets/billing.notificationsettings.json +6 -0
  273. package/generated/v1/snippets/billing.notificationsettingsupdate.json +6 -0
  274. package/generated/v1/snippets/billing.portal.json +6 -0
  275. package/generated/v1/snippets/billing.projects.json +6 -0
  276. package/generated/v1/snippets/billing.purchase.json +6 -0
  277. package/generated/v1/snippets/billing.quote.json +6 -0
  278. package/generated/v1/snippets/billing.reconcile.json +6 -0
  279. package/generated/v1/snippets/billing.refundrequest.json +6 -0
  280. package/generated/v1/snippets/billing.refundresolution.json +6 -0
  281. package/generated/v1/snippets/billing.summary.json +6 -0
  282. package/generated/v1/snippets/context.read.json +6 -0
  283. package/generated/v1/snippets/credentials.connection.json +6 -0
  284. package/generated/v1/snippets/credentials.create.json +1 -0
  285. package/generated/v1/snippets/credentials.list.json +1 -0
  286. package/generated/v1/snippets/credentials.revoke.json +1 -0
  287. package/generated/v1/snippets/credentials.rotate.json +1 -0
  288. package/generated/v1/snippets/endpoints.acknowledgeSigningKeyRotation.json +6 -0
  289. package/generated/v1/snippets/endpoints.create.json +1 -0
  290. package/generated/v1/snippets/endpoints.disable.json +1 -0
  291. package/generated/v1/snippets/{endpoints.verify.json → endpoints.enable.json} +4 -3
  292. package/generated/v1/snippets/endpoints.list.json +1 -0
  293. package/generated/v1/snippets/endpoints.read.json +1 -0
  294. package/generated/v1/snippets/endpoints.revealSigningSecret.json +6 -0
  295. package/generated/v1/snippets/endpoints.rotateSigningKey.json +1 -0
  296. package/generated/v1/snippets/endpoints.update.json +1 -0
  297. package/generated/v1/snippets/executions.list.json +6 -0
  298. package/generated/v1/snippets/exports.cancel.json +6 -0
  299. package/generated/v1/snippets/exports.create.json +6 -0
  300. package/generated/v1/snippets/exports.part.json +6 -0
  301. package/generated/v1/snippets/exports.read.json +6 -0
  302. package/generated/v1/snippets/forecast.read.json +1 -0
  303. package/generated/v1/snippets/forecast.windows.json +1 -0
  304. package/generated/v1/snippets/grants.list.json +1 -0
  305. package/generated/v1/snippets/grants.revoke.json +1 -0
  306. package/generated/v1/snippets/journey/await-occurrence.json +6 -0
  307. package/generated/v1/snippets/journey/check-credential.json +6 -0
  308. package/generated/v1/snippets/journey/check-service.json +6 -0
  309. package/generated/v1/snippets/journey/clean-up.json +6 -0
  310. package/generated/v1/snippets/journey/create-destination.json +6 -0
  311. package/generated/v1/snippets/journey/create-one-shot.json +6 -0
  312. package/generated/v1/snippets/journey/read-attempt.json +6 -0
  313. package/generated/v1/snippets/journey/read-schedule.json +6 -0
  314. package/generated/v1/snippets/journey/replay-one-shot.json +6 -0
  315. package/generated/v1/snippets/meta.scaffold.json +1 -0
  316. package/generated/v1/snippets/meta.version.json +1 -0
  317. package/generated/v1/snippets/occurrences.list.json +1 -0
  318. package/generated/v1/snippets/occurrences.read.json +1 -0
  319. package/generated/v1/snippets/occurrences.redeliver.json +1 -0
  320. package/generated/v1/snippets/organizations.close.json +6 -0
  321. package/generated/v1/snippets/organizations.lifecycle.json +6 -0
  322. package/generated/v1/snippets/organizations.restore.json +6 -0
  323. package/generated/v1/snippets/projects.archive.json +6 -0
  324. package/generated/v1/snippets/projects.create.json +1 -0
  325. package/generated/v1/snippets/projects.delete.json +6 -0
  326. package/generated/v1/snippets/projects.list.json +1 -0
  327. package/generated/v1/snippets/projects.rename.json +6 -0
  328. package/generated/v1/snippets/schedules.archive.json +1 -0
  329. package/generated/v1/snippets/schedules.cancel.json +1 -0
  330. package/generated/v1/snippets/schedules.create.json +4 -3
  331. package/generated/v1/snippets/schedules.list.json +1 -0
  332. package/generated/v1/snippets/schedules.pause.json +1 -0
  333. package/generated/v1/snippets/schedules.read.json +1 -0
  334. package/generated/v1/snippets/schedules.replace.json +4 -3
  335. package/generated/v1/snippets/schedules.resume.json +1 -0
  336. package/generated/v1/snippets/sinkdeliveries.list.json +1 -0
  337. package/generated/v1/snippets/sinkdeliveries.read.json +1 -0
  338. package/generated/v1/snippets/testsinks.configure.json +1 -0
  339. package/generated/v1/snippets/testsinks.create.json +1 -0
  340. package/generated/v1/snippets/testsinks.delete.json +1 -0
  341. package/generated/v1/snippets/testsinks.list.json +1 -0
  342. package/generated/v1/snippets/testsinks.read.json +1 -0
  343. package/generated/v1/snippets/timing.summary.json +6 -0
  344. package/generated/v1/snippets/usage.read.json +1 -0
  345. package/package.json +2 -7
  346. package/dist/callback-ownership.d.ts +0 -58
  347. package/dist/callback-ownership.js +0 -71
  348. package/generated/v1/callback-endpoint-verification.json +0 -59
  349. /package/generated/v1/schemas/{endpoints.verify.response-headers.json → endpoints.acknowledgeSigningKeyRotation.response-headers.json} +0 -0
@@ -1,7 +1,7 @@
1
- import { z } from "zod";
1
+ import { z } from "./zod.js";
2
2
  import { attemptIdSchema, deliveryIdSchema, endpointIdSchema, occurrenceIdSchema, scheduleIdSchema, } from "./identifiers.js";
3
3
  import { CALLBACK_ATTEMPT_DIAGNOSTICS } from "./delivery.js";
4
- import { pageSchema, versionSchema } from "./protocol.js";
4
+ import { pageRequestSchema, pageSchema, versionSchema, } from "./protocol.js";
5
5
  import { callbackTriggerSchema, } from "./signatures.js";
6
6
  import { durationSecondsSchema, timeZoneSchema, timestampSchema } from "./time.js";
7
7
  /**
@@ -55,29 +55,87 @@ export const scheduleStatusSchema = z.enum(SCHEDULE_STATUSES);
55
55
  */
56
56
  export const LATENCY_CLASSES = ["realtime", "bulk"];
57
57
  export const latencyClassSchema = z.enum(LATENCY_CLASSES);
58
+ /**
59
+ * What happens when an occurrence reaches its first attempt after its planned
60
+ * instant.
61
+ *
62
+ * `skipAhead` never carries a recurring first arrival across its next planned
63
+ * instant (and a shorter custom window may retire it earlier). `catchUp`
64
+ * admits one first arrival while it is still inside the schedule's staleness
65
+ * window, even after later recurrence instants passed. A one-shot has no next
66
+ * recurrence, so either explicit policy uses its staleness window and its
67
+ * default is `catchUp`. Neither choice applies to an occurrence already on its
68
+ * retry ladder: that work arrived, failed, and keeps the retry/give-up policy
69
+ * it started under.
70
+ */
71
+ export const LATENESS_POLICIES = ["skipAhead", "catchUp"];
72
+ export const latenessPolicySchema = z.enum(LATENESS_POLICIES);
73
+ /** The inherited one-shot staleness window: twenty-four hours. */
74
+ export const DEFAULT_ONE_SHOT_MAX_STALENESS_SECONDS = 86_400;
75
+ /**
76
+ * The per-schedule lateness choices a caller may state.
77
+ *
78
+ * Both members are optional so a caller can override only the part whose
79
+ * default does not fit. The policy defaults from recurrence kind. An omitted
80
+ * staleness bound uses the recurrence's own default: twenty-four hours for a
81
+ * one-shot, and one recurrence interval for a recurring schedule.
82
+ */
83
+ export const scheduleLatenessRequestSchema = z
84
+ .object({
85
+ maxStalenessSeconds: durationSecondsSchema
86
+ .positive("must be at least 1 second")
87
+ .max(Number.MAX_SAFE_INTEGER, "must be a safely representable duration")
88
+ .optional(),
89
+ policy: latenessPolicySchema.optional(),
90
+ })
91
+ .strict();
92
+ /**
93
+ * Lateness settings as a schedule reads back.
94
+ *
95
+ * The policy is always effective and therefore required. The numeric member
96
+ * remains optional because it records a per-schedule override, not a duplicated
97
+ * copy of a default that can be derived from the recurrence.
98
+ */
99
+ export const scheduleLatenessSchema = scheduleLatenessRequestSchema.extend({
100
+ policy: latenessPolicySchema,
101
+ });
58
102
  /* -------------------------------------------------------------------------- */
59
103
  /* Delivery duration */
60
104
  /* -------------------------------------------------------------------------- */
61
105
  /**
62
- * How long a receiver may take to answer, as a ladder rather than a number.
106
+ * The durations this product used to sell, one rung at a time.
107
+ *
108
+ * **Kept, and no longer the whole of what is accepted** (FXO-2258). The ladder
109
+ * was the argument that 47 seconds is a question with no answer — past the
110
+ * point where a delivery may occupy a partition slot, short of the point where
111
+ * it is worth carrying separately. The operator overruled it after the walk:
112
+ * "This can have a completely user definable timeout, they don't cost us hardly
113
+ * anything, but we should cap at something anyway, 24h is still fine."
63
114
  *
64
- * **A ladder because every rung is a different product.** The first two are the
65
- * ordinary path: a delivery is carried inside the partition's own dispatch and
66
- * holds one of six in-flight slots for its whole duration, so the cost of a slow
67
- * receiver is paid by every other schedule in that
68
- * `(organization, project, environment)`. The rungs above are carried elsewhere
69
- * and priced, because that is the only honest way to sell them.
115
+ * What the rungs were actually carrying survives without them. The line that
116
+ * matters commercially is {@link FREE_DELIVERY_DURATION_SECONDS} — above thirty
117
+ * seconds a delivery is carried outside the partition and needs a plan that
118
+ * allows it — and that line is a comparison, not a membership test. The line
119
+ * that matters technically is {@link RESPONSE_DELIVERY_MAX_DURATION_SECONDS},
120
+ * which is also a comparison. Neither ever needed eight fixed values to be
121
+ * expressible; the ladder was how they were *sold*, and per-rung pricing is
122
+ * what the operator withdrew.
70
123
  *
71
- * **Deliberately not a free-form integer.** A caller that could ask for 47
72
- * seconds would be asking a question this product has no answer to: 47 is past
73
- * the point where a delivery may occupy a partition slot and short of the point
74
- * where it is worth carrying separately. The ladder is what makes "which side of
75
- * the line is this" a decision somebody took rather than a comparison somebody
76
- * wrote.
124
+ * The list stays because a schedule saved on a rung must keep working, because
125
+ * the delivery-class rules are still exercised against exactly these values,
126
+ * and because the dashboard offers them as the shortcuts most people want.
77
127
  */
78
128
  export const DELIVERY_DURATION_TIERS = [
79
129
  5, 30, 300, 900, 1_800, 3_600, 43_200, 86_400,
80
130
  ];
131
+ /**
132
+ * The longest duration any delivery may be given, in seconds.
133
+ *
134
+ * Twenty-four hours, which is the ladder's own top rung — so widening the field
135
+ * to a free-form integer admits nothing that was not already for sale. "24h is
136
+ * still fine."
137
+ */
138
+ export const MAX_DELIVERY_DURATION_SECONDS = 86_400;
81
139
  /**
82
140
  * What a schedule gets without asking, in seconds.
83
141
  *
@@ -134,8 +192,26 @@ export const FREE_DELIVERY_DURATION_SECONDS = 30;
134
192
  * than thirty minutes has to do is answer immediately and call us back.
135
193
  */
136
194
  export const RESPONSE_DELIVERY_MAX_DURATION_SECONDS = 1_800;
195
+ /**
196
+ * How long the receiver may take to answer, in whole seconds.
197
+ *
198
+ * **A bounded integer since FXO-2258, and every value the enum accepted is
199
+ * still accepted.** A widening is the one shape of contract change that cannot
200
+ * break a caller: a request that was valid before is valid now, and a stored
201
+ * schedule reads back and re-saves unchanged.
202
+ *
203
+ * The two bounds that are real are still enforced, and neither of them was ever
204
+ * the enum's doing: `maxDurationSeconds` above
205
+ * {@link RESPONSE_DELIVERY_MAX_DURATION_SECONDS} is refused unless `completion`
206
+ * is `callback` (checked in `scheduleRequestSchema`'s refinement, because it is
207
+ * a relation between two fields), and above
208
+ * {@link FREE_DELIVERY_DURATION_SECONDS} it needs a plan that allows it.
209
+ */
137
210
  export const deliveryDurationSecondsSchema = z
138
- .literal(DELIVERY_DURATION_TIERS)
211
+ .number()
212
+ .int("must be a whole number of seconds")
213
+ .positive("must be at least 1 second")
214
+ .max(MAX_DELIVERY_DURATION_SECONDS, `must be at most ${String(MAX_DELIVERY_DURATION_SECONDS)} seconds`)
139
215
  .describe("How long the receiver may take to answer, in seconds");
140
216
  /** Whether a duration is past the point where it needs a plan that allows it. */
141
217
  export function isLongRunningDuration(seconds) {
@@ -247,6 +323,89 @@ export const OCCURRENCE_STATES = [
247
323
  "skipped_stale",
248
324
  ];
249
325
  export const occurrenceStateSchema = z.enum(OCCURRENCE_STATES);
326
+ /**
327
+ * One project-wide walk over retained occurrences, newest first.
328
+ *
329
+ * There is deliberately no order parameter: the Executions page answers what
330
+ * happened most recently, and making that fixed lets its index and cursor say
331
+ * the same thing. The date range is half-open, like every other time window in
332
+ * this contract, so adjacent requests neither overlap nor leave a gap.
333
+ */
334
+ export const projectOccurrenceListRequestSchema = pageRequestSchema
335
+ .extend({
336
+ endpoint: endpointIdSchema.optional(),
337
+ from: timestampSchema.optional(),
338
+ outcome: occurrenceStateSchema.optional(),
339
+ schedule: scheduleIdSchema.optional(),
340
+ to: timestampSchema.optional(),
341
+ })
342
+ .refine((request) => request.from === undefined ||
343
+ request.to === undefined ||
344
+ Date.parse(request.from) < Date.parse(request.to), {
345
+ message: "must be later than from",
346
+ path: ["to"],
347
+ });
348
+ const PROJECT_OCCURRENCE_LIST_PARAMETERS = [
349
+ "cursor",
350
+ "limit",
351
+ "endpoint",
352
+ "from",
353
+ "outcome",
354
+ "schedule",
355
+ "to",
356
+ ];
357
+ /** Read the project occurrence filters exactly as REST and MCP send them. */
358
+ export function parseProjectOccurrenceListRequest(query) {
359
+ const errors = [];
360
+ const raw = {};
361
+ for (const field of PROJECT_OCCURRENCE_LIST_PARAMETERS) {
362
+ const values = query.getAll(field);
363
+ if (values.length > 1) {
364
+ errors.push({
365
+ pointer: `/${field}`,
366
+ reason: "must be given at most once",
367
+ });
368
+ continue;
369
+ }
370
+ const value = values[0];
371
+ if (value === undefined)
372
+ continue;
373
+ raw[field] =
374
+ field === "limit" && /^-?\d+$/u.test(value) ? Number(value) : value;
375
+ }
376
+ if (errors.length > 0)
377
+ return { errors, ok: false };
378
+ const parsed = projectOccurrenceListRequestSchema.safeParse(raw);
379
+ if (parsed.success)
380
+ return { ok: true, page: parsed.data };
381
+ return {
382
+ errors: parsed.error.issues.map((issue) => ({
383
+ pointer: `/${issue.path.join("/")}`,
384
+ reason: issue.message,
385
+ })),
386
+ ok: false,
387
+ };
388
+ }
389
+ /**
390
+ * The exact operation and filters a project occurrence cursor belongs to.
391
+ *
392
+ * Cursor and limit do not affect which ordered collection is being walked, so
393
+ * neither belongs in the binding. Every actual filter does. Optional values
394
+ * stay absent rather than becoming empty strings, which are invalid inputs and
395
+ * must never collide with an unfiltered request.
396
+ */
397
+ export function projectOccurrenceCursorBinding(request) {
398
+ return {
399
+ operation: "executions.list",
400
+ parameters: {
401
+ ...(request.endpoint === undefined ? {} : { endpoint: request.endpoint }),
402
+ ...(request.from === undefined ? {} : { from: request.from }),
403
+ ...(request.outcome === undefined ? {} : { outcome: request.outcome }),
404
+ ...(request.schedule === undefined ? {} : { schedule: request.schedule }),
405
+ ...(request.to === undefined ? {} : { to: request.to }),
406
+ },
407
+ };
408
+ }
250
409
  /**
251
410
  * The states an occurrence never leaves.
252
411
  *
@@ -296,7 +455,7 @@ export const ATTEMPT_REMEDIATIONS = [
296
455
  "none",
297
456
  "review_retry_policy",
298
457
  "inspect_receiver",
299
- "verify_endpoint",
458
+ "review_endpoint",
300
459
  "replace_payload",
301
460
  "contact_support",
302
461
  "review_before_redelivery",
@@ -320,7 +479,7 @@ export function remediationForAttempt(diagnostic) {
320
479
  return "none";
321
480
  case "endpoint_unavailable":
322
481
  case "endpoint_unsafe":
323
- return "verify_endpoint";
482
+ return "review_endpoint";
324
483
  case "http_refused":
325
484
  case "http_terminal":
326
485
  // The same advice for two different futures, deliberately. Both mean the
@@ -409,17 +568,37 @@ export const SCHEDULING_BOUNDS = {
409
568
  * Attempts made against one occurrence before it is terminal, including the
410
569
  * first. Bounded so a permanently broken receiver cannot make one occurrence
411
570
  * cost an unbounded amount of work.
571
+ *
572
+ * **Eight, and the number is load-bearing against a second limit rather than
573
+ * on its own.** `MAX_BUDGETED_DELIVERY_FAILURES` stops an occurrence after
574
+ * five failures the *receiver* caused; a rate limit and this product's own
575
+ * infrastructure faults are exempt from that count. The gap between the two
576
+ * is what the exemption spends — an occurrence throttled twice still has
577
+ * five receiver failures left. Lowering this to five was considered during
578
+ * the W4-10 walk and rejected once the pair was understood: it would make
579
+ * both limits fire on the same attempt and leave the exemption buying
580
+ * nothing.
412
581
  */
413
582
  attemptsPerOccurrence: 8,
414
583
  /**
415
584
  * The longest fixed interval a recurring schedule may ask for, in seconds.
416
585
  *
417
- * Thirty days, which is the copied scheduler's `MAX_INTERVAL_SECONDS` kept
418
- * rather than a new number invented beside it, and its reason is the
419
- * platform's rather than the product's: a Durable Object alarm is not
420
- * arbitrarily far in the future.
586
+ * One year. **The thirty days this replaces had no reason of its own** — it
587
+ * was the copied scheduler's `MAX_INTERVAL_SECONDS`, carried over on the
588
+ * belief that "a Durable Object alarm is not arbitrarily far in the future".
589
+ * Cloudflare documents no maximum distance for `setAlarm()`, and the belief
590
+ * predates the PostgreSQL authority besides: the durable fact is a row, and
591
+ * the alarm is only what wakes a bucket to read it.
592
+ *
593
+ * What makes a distant alarm safe is not the alarm. `TimerBucketRepairer`
594
+ * asks PostgreSQL for occurrences whose instant has passed and re-signals
595
+ * their bucket, on committed truth, never asking whether an alarm was armed
596
+ * — so an alarm that is somehow lost costs lateness rather than the run.
597
+ *
598
+ * Widening a bound is additive by `classifyChange` and narrowing it is
599
+ * breaking, so this direction is the safe one to move in.
421
600
  */
422
- everySecondsMax: 30 * 24 * 60 * 60,
601
+ everySecondsMax: 365 * 24 * 60 * 60,
423
602
  /**
424
603
  * The shortest fixed interval a recurring schedule may ask for, in seconds.
425
604
  *
@@ -573,6 +752,10 @@ export const recurrenceSchema = z.discriminatedUnion("kind", [
573
752
  timeZone: timeZoneSchema,
574
753
  }),
575
754
  ]);
755
+ /** The customer-safe default for a schedule's recurrence kind. */
756
+ export function defaultLatenessPolicyForRecurrence(kind) {
757
+ return kind === "at" ? "catchUp" : "skipAhead";
758
+ }
576
759
  /* -------------------------------------------------------------------------- */
577
760
  /* Public resources */
578
761
  /* -------------------------------------------------------------------------- */
@@ -584,6 +767,23 @@ export const recurrenceSchema = z.discriminatedUnion("kind", [
584
767
  * endpoint id is useful definition metadata; the encrypted payload is content
585
768
  * and stays only in the authoritative partition.
586
769
  */
770
+ /**
771
+ * A schedule's name: for people, never an identifier, and free to collide.
772
+ *
773
+ * Generated on save when nobody types one (W4-14, FXO-2255) — "Every 60s →
774
+ * flowxo.com" — and stored as any other name, so editing the recurrence
775
+ * later rewrites nothing. Two schedules may share a name: two on the same
776
+ * cadence against the same host both generate the same one, and the operator
777
+ * chose to let them collide, with the other columns and the identifier
778
+ * telling them apart. Endpoints get the opposite rule.
779
+ */
780
+ /** The most a schedule's name may hold; the generated one is cut to fit it. */
781
+ export const SCHEDULE_NAME_MAX_LENGTH = 120;
782
+ export const scheduleNameSchema = z
783
+ .string()
784
+ .trim()
785
+ .min(1, "must not be blank")
786
+ .max(SCHEDULE_NAME_MAX_LENGTH, `must be at most ${String(SCHEDULE_NAME_MAX_LENGTH)} characters`);
587
787
  export const scheduleSchema = z.object({
588
788
  /**
589
789
  * When the schedule was filed away, and absent until it is.
@@ -596,6 +796,20 @@ export const scheduleSchema = z.object({
596
796
  * terminal status — archiving is filing, not stopping.
597
797
  */
598
798
  archivedAt: timestampSchema.optional(),
799
+ /**
800
+ * The payload the schedule carries, as it was given.
801
+ *
802
+ * Present on a read when the schedule has one; left off listings, which are
803
+ * served from a projection that holds a summary. **Readable by anyone who
804
+ * can read the schedule** — the dashboard, the API, the SDK and MCP alike —
805
+ * since FXO-2243, chosen with the cost stated: a payload somebody put a
806
+ * secret in is readable by anything holding a read key, and appears in an
807
+ * agent's tool history. The write-only custody this replaces answered that
808
+ * with a blanket rule over the whole body — "I've never used a platform
809
+ * where my configured payload was hidden from me" — and W4-13 answers it
810
+ * per field instead, with a header value that is a secret marked as one.
811
+ */
812
+ data: z.json().optional(),
599
813
  createdAt: timestampSchema,
600
814
  /**
601
815
  * The delivery settings in force, with the defaults filled in.
@@ -626,9 +840,33 @@ export const scheduleSchema = z.object({
626
840
  .optional(),
627
841
  /** The verified reusable callback endpoint this schedule delivers to. */
628
842
  endpoint: endpointIdSchema.optional(),
843
+ /**
844
+ * Where the schedule delivers, as an address (W4-14, FXO-2255).
845
+ *
846
+ * Beside the identifier rather than instead of it, on the listing and the
847
+ * read alike, so every caller — dashboard, SDK, agent — sees a readable
848
+ * destination without a second request. Present when the schedule has an
849
+ * endpoint the service could name; absent on a timer, and on a row whose
850
+ * endpoint is no longer readable.
851
+ */
852
+ endpointUrl: z.string().url().optional(),
629
853
  generation: generationSchema,
630
854
  id: scheduleIdSchema,
631
855
  latencyClass: latencyClassSchema,
856
+ /**
857
+ * What this schedule does with an occurrence that reaches its first attempt
858
+ * late. Present on every schedule so a caller never has to duplicate the
859
+ * recurrence-sensitive policy default. `maxStalenessSeconds` is present only
860
+ * when this schedule overrides its recurrence's window.
861
+ */
862
+ lateness: scheduleLatenessSchema,
863
+ /**
864
+ * The schedule's name. Every schedule the service has written since W4-14
865
+ * has one — generated on save when none was given — and it is optional on
866
+ * the wire only so a row from before the column, or a reader holding a
867
+ * projection the migration has not reached, parses rather than fails.
868
+ */
869
+ name: scheduleNameSchema.optional(),
632
870
  partitionKey: partitionKeySchema.optional(),
633
871
  /**
634
872
  * How this schedule recurs, in the vocabulary it will keep being read in.
@@ -643,6 +881,24 @@ export const scheduleSchema = z.object({
643
881
  * was converted to an instant once, at acceptance, and reporting the delay
644
882
  * back would describe a schedule whose next fire moves every time it is read.
645
883
  */
884
+ /**
885
+ * When this schedule next fires, and absent when nothing is on the books.
886
+ *
887
+ * **The earliest unsettled occurrence, which is a fact rather than a
888
+ * projection** (FXO-2283). It is absent for a schedule that will not fire
889
+ * again — cancelled, archived, or a one-shot that has run — and absent is the
890
+ * truth about those rather than a value withheld: there is no instant to
891
+ * report, and reporting the last one would read as a plan.
892
+ *
893
+ * **This is the one field on a listing row that does not come from the
894
+ * projection.** The page comment below says a listing holds a route and a
895
+ * summary rather than every partition's live occurrence, and that is still
896
+ * true of the *detail* an occurrence carries — its state, its attempts, its
897
+ * identifier. This is one instant, taken by a lateral against a partial index
898
+ * built for it, and it is here because "when does this next run" is the
899
+ * question a list of schedules is read to answer.
900
+ */
901
+ nextDueAt: timestampSchema.optional(),
646
902
  recurrence: recurrenceSchema,
647
903
  status: scheduleStatusSchema,
648
904
  updatedAt: timestampSchema,
@@ -659,6 +915,32 @@ export const scheduleSchema = z.object({
659
915
  * is authoritative, and detail is one call away.
660
916
  */
661
917
  export const schedulePageSchema = pageSchema(scheduleSchema);
918
+ function hasCompleteTimingEvidence(value) {
919
+ return ((value.firstAttemptStartedAt === undefined) ===
920
+ (value.firstAttemptLatenessMs === undefined) &&
921
+ (value.firstAttemptStartedAt === undefined) ===
922
+ (value.timingPolicyVersion === undefined));
923
+ }
924
+ function hasConsistentTimingEligibility(value) {
925
+ return value.timingEligibility === undefined
926
+ ? value.timingEligibilityReason === undefined
927
+ : value.timingEligibility === "eligible"
928
+ ? value.timingEligibilityReason === undefined
929
+ : value.timingEligibilityReason !== undefined;
930
+ }
931
+ const timingEvidenceShape = {
932
+ /** Signed difference from `plannedFor`; a negative value is an early send. */
933
+ firstAttemptLatenessMs: z.int().optional(),
934
+ /** Earliest trusted outbound start across this occurrence's attempts. */
935
+ firstAttemptStartedAt: timestampSchema.optional(),
936
+ /** Absent on records that predate timing evidence, which means unknown. */
937
+ timingEligibility: z
938
+ .enum(["eligible", "customer_blocked", "platform_blocked"])
939
+ .optional(),
940
+ timingEligibilityReason: z.string().min(1).max(128).optional(),
941
+ /** Policy pinned when trusted first-attempt timing evidence is recorded. */
942
+ timingPolicyVersion: z.string().min(1).max(64).optional(),
943
+ };
662
944
  /**
663
945
  * One concrete occurrence of a schedule.
664
946
  *
@@ -670,6 +952,7 @@ export const schedulePageSchema = pageSchema(scheduleSchema);
670
952
  export const occurrenceSchema = z
671
953
  .object({
672
954
  attemptCount: z.number().int().nonnegative(),
955
+ ...timingEvidenceShape,
673
956
  generation: generationSchema,
674
957
  id: occurrenceIdSchema,
675
958
  /** When the occurrence was planned to fire. Never earlier than this. */
@@ -689,6 +972,14 @@ export const occurrenceSchema = z
689
972
  (occurrence.settledAt !== undefined), {
690
973
  message: "settledAt must be present for a terminal state and absent otherwise",
691
974
  path: ["settledAt"],
975
+ })
976
+ .refine(hasCompleteTimingEvidence, {
977
+ message: "firstAttemptStartedAt, firstAttemptLatenessMs and timingPolicyVersion must be present together",
978
+ path: ["firstAttemptStartedAt"],
979
+ })
980
+ .refine(hasConsistentTimingEligibility, {
981
+ message: "timingEligibilityReason is required for a blocked population and absent otherwise",
982
+ path: ["timingEligibilityReason"],
692
983
  });
693
984
  /**
694
985
  * One delivery attempt against one occurrence.
@@ -753,6 +1044,7 @@ export const attemptSchema = z
753
1044
  .nonnegative()
754
1045
  .max(ATTEMPT_DURATION_MAX_MS)
755
1046
  .optional(),
1047
+ ...timingEvidenceShape,
756
1048
  id: attemptIdSchema,
757
1049
  occurrence: occurrenceIdSchema,
758
1050
  /** Stable next-action code paired with a diagnostic when one is known. */
@@ -802,11 +1094,24 @@ export const attemptSchema = z
802
1094
  attempt.responseStatus === undefined), {
803
1095
  message: "classification metadata must be absent while an attempt is in flight",
804
1096
  path: ["state"],
1097
+ })
1098
+ .refine(hasCompleteTimingEvidence, {
1099
+ message: "firstAttemptStartedAt, firstAttemptLatenessMs and timingPolicyVersion must be present together",
1100
+ path: ["firstAttemptStartedAt"],
1101
+ })
1102
+ .refine(hasConsistentTimingEligibility, {
1103
+ message: "timingEligibilityReason is required for a blocked population and absent otherwise",
1104
+ path: ["timingEligibilityReason"],
805
1105
  });
806
1106
  /** An authoritative page over one schedule's retained occurrences. */
807
1107
  export const occurrencePageSchema = pageSchema(occurrenceSchema).extend({
808
1108
  freshness: z.object({ kind: z.literal("authoritative") }),
809
1109
  });
1110
+ /** A project-wide occurrence page and its plan's normal history boundary. */
1111
+ export const projectOccurrencePageSchema = pageSchema(occurrenceSchema).extend({
1112
+ freshness: z.object({ kind: z.literal("authoritative") }),
1113
+ retention: z.object({ from: timestampSchema }),
1114
+ });
810
1115
  /** An authoritative occurrence detail response. */
811
1116
  export const occurrenceViewSchema = z.object({
812
1117
  freshness: z.object({ kind: z.literal("authoritative") }),
@@ -877,8 +1182,8 @@ export const scheduleRequestSchema = z
877
1182
  /**
878
1183
  * Customer data placed in the signed callback envelope.
879
1184
  *
880
- * Any JSON value is accepted, including `null`, arrays and scalars. It is
881
- * never returned by schedule reads or listings. The accepting adapter
1185
+ * Any JSON value is accepted, including `null`, arrays and scalars. A
1186
+ * schedule read returns it; listings leave it off. The accepting adapter
882
1187
  * serializes it once, enforces `SCHEDULING_BOUNDS.payloadBytes` against
883
1188
  * those UTF-8 bytes, then encrypts those same bytes before persistence.
884
1189
  */
@@ -904,16 +1209,15 @@ export const scheduleRequestSchema = z
904
1209
  *
905
1210
  * **Grouped rather than three sibling fields**, because two of the three are
906
1211
  * meaningless without the first. `onOverlap` only answers a question a long
907
- * run can ask, and a caller who set it beside a five-second delivery would
908
- * be configuring something that cannot happen. Nesting makes the dependency
909
- * visible in the shape instead of only in a refusal message.
1212
+ * run can ask; beside a five-second delivery it is stored and never
1213
+ * consulted. Nesting makes the dependency visible in the shape.
910
1214
  */
911
1215
  delivery: z
912
1216
  .object({
913
1217
  /**
914
1218
  * What to do when this schedule comes due and its previous occurrence
915
- * has not finished. Only meaningful on a recurring, long-running
916
- * schedule; refused elsewhere rather than ignored.
1219
+ * has not finished. Consulted only on a recurring schedule whose runs
1220
+ * can outlast their interval; accepted and ignored elsewhere.
917
1221
  */
918
1222
  onOverlap: deliveryOverlapPolicySchema.optional(),
919
1223
  /**
@@ -922,8 +1226,11 @@ export const scheduleRequestSchema = z
922
1226
  */
923
1227
  completion: deliveryCompletionModeSchema.optional(),
924
1228
  /**
925
- * How long the receiver may take to answer, in seconds. One of
926
- * `DELIVERY_DURATION_TIERS`.
1229
+ * How long the receiver may take to answer, in whole seconds, up to
1230
+ * `MAX_DELIVERY_DURATION_SECONDS`. Above
1231
+ * `RESPONSE_DELIVERY_MAX_DURATION_SECONDS` the receiver has to answer
1232
+ * immediately and call the completion URL instead, which is what
1233
+ * `completion: "callback"` says.
927
1234
  */
928
1235
  maxDurationSeconds: deliveryDurationSecondsSchema.optional(),
929
1236
  })
@@ -965,6 +1272,17 @@ export const scheduleRequestSchema = z
965
1272
  * than obtained more of the service.
966
1273
  */
967
1274
  latencyClass: latencyClassSchema.optional(),
1275
+ /**
1276
+ * What to do when an occurrence reaches its first attempt late.
1277
+ *
1278
+ * Omitted policy defaults to `catchUp` for `at` and `delaySeconds`, and to
1279
+ * `skipAhead` for `everySeconds` and `cron`. `maxStalenessSeconds` overrides
1280
+ * the schedule-derived window with a positive whole number of seconds.
1281
+ * Retries are exempt: once an occurrence has made an attempt, retry and
1282
+ * give-up policy decide its outcome instead of measuring again from the
1283
+ * original planned instant.
1284
+ */
1285
+ lateness: scheduleLatenessRequestSchema.optional(),
968
1286
  /**
969
1287
  * A fairness lane within the caller's own project and environment.
970
1288
  *
@@ -983,6 +1301,15 @@ export const scheduleRequestSchema = z
983
1301
  * somebody asks otherwise.
984
1302
  */
985
1303
  partitionKey: partitionKeySchema.optional(),
1304
+ /**
1305
+ * A name for people (W4-14, FXO-2255). On a create, omitted means the
1306
+ * service generates one from the type, period and target domain —
1307
+ * "Every 60s → flowxo.com" — and stores it. On a replace, omitted means
1308
+ * the stored name stays as it is: a generated name is recomputed on
1309
+ * nothing, so editing the recurrence does not silently rewrite a name
1310
+ * the person kept or changed. Names may collide.
1311
+ */
1312
+ name: scheduleNameSchema.optional(),
986
1313
  /**
987
1314
  * The IANA timezone `cron` is read against. `UTC` when omitted.
988
1315
  *
@@ -1024,30 +1351,15 @@ export const scheduleRequestSchema = z
1024
1351
  path: ["timeZone"],
1025
1352
  });
1026
1353
  }
1027
- if (request.delivery?.onOverlap !== undefined) {
1028
- // A one-shot has nothing to overlap with. Refused rather than ignored, for
1029
- // the reason `timeZone` is: an ignored field is a misunderstanding the
1030
- // customer cannot see, and this one would look like a guarantee.
1031
- if (request.cron === undefined && request.everySeconds === undefined) {
1032
- context.addIssue({
1033
- code: "custom",
1034
- message: "`onOverlap` is only meaningful with `cron` or `everySeconds`",
1035
- path: ["delivery", "onOverlap"],
1036
- });
1037
- }
1038
- // Only checked when the duration is in *this* request. A replace may carry
1039
- // `onOverlap` while the stored schedule holds the duration, exactly as it
1040
- // may carry `data` while the stored schedule holds the endpoint, and the
1041
- // partition is where a request is resolved against what is stored.
1042
- const duration = request.delivery.maxDurationSeconds;
1043
- if (duration !== undefined && !isLongRunningDuration(duration)) {
1044
- context.addIssue({
1045
- code: "custom",
1046
- message: `\`onOverlap\` is only meaningful above ${String(FREE_DELIVERY_DURATION_SECONDS)} seconds, where a run can still be going when the next one is due`,
1047
- path: ["delivery", "onOverlap"],
1048
- });
1049
- }
1050
- }
1354
+ // **`onOverlap` is accepted wherever it is given, and ignored where it
1355
+ // cannot apply** — a one-shot, or a run too short to outlast its own
1356
+ // interval. It used to be refused in both cases, and the refusal became a
1357
+ // trap: every stored schedule carries the default policy, the dashboard's
1358
+ // editor sent it back on every save, and a five-second delivery was
1359
+ // refused for a field nobody had touched, under a collapsed section. The
1360
+ // operator's ruling on the W4-10 walk: "so what if it's not meaningful —
1361
+ // then we ignore it." A policy that cannot fire is not a guarantee anyone
1362
+ // can rely on, and the schedule's read reports what is stored.
1051
1363
  // Held open past the point a held-open request survives. Checked against the
1052
1364
  // *derived* pair rather than against what the body names, because
1053
1365
  // `completion` defaults to `response`: a caller who asks for twelve hours and
@@ -1067,6 +1379,20 @@ export const scheduleRequestSchema = z
1067
1379
  });
1068
1380
  }
1069
1381
  });
1382
+ /** Resolve the lateness settings a create or replacement request runs under. */
1383
+ export function deriveLatenessSettings(request) {
1384
+ const recurrenceKind = request.cron !== undefined
1385
+ ? "cron"
1386
+ : request.everySeconds !== undefined
1387
+ ? "every"
1388
+ : "at";
1389
+ const policy = request.lateness?.policy ??
1390
+ defaultLatenessPolicyForRecurrence(recurrenceKind);
1391
+ const maxStalenessSeconds = request.lateness?.maxStalenessSeconds;
1392
+ return maxStalenessSeconds === undefined
1393
+ ? { policy }
1394
+ : { maxStalenessSeconds, policy };
1395
+ }
1070
1396
  /**
1071
1397
  * The delivery settings a request runs under: the caller's, or the defaults.
1072
1398
  *