@yielded/agent 0.1.0-beta.173 → 0.1.0-beta.175

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 (266) hide show
  1. package/dist/{Admin-BX9ns28E.d.mts → Admin-Qa3I1oA5.d.mts} +61 -21
  2. package/dist/{Agent-9aBoIi74.d.mts → Agent-B39jRnFv.d.mts} +2 -2
  3. package/dist/{Agent-0WvBWWbM.mjs → Agent-BiRbr73L.mjs} +7 -6
  4. package/dist/Agent-BiRbr73L.mjs.map +1 -0
  5. package/dist/{AgentRuntime-BfP14534.d.mts → AgentRuntime-CHDyRbSf.d.mts} +12 -12
  6. package/dist/{AgentRuntime-CQ-qcETl.mjs → AgentRuntime-DaDKMasp.mjs} +290 -273
  7. package/dist/AgentRuntime-DaDKMasp.mjs.map +1 -0
  8. package/dist/{Digest-DCuPlhNk.mjs → Digest-C3EODOee.mjs} +42 -15
  9. package/dist/Digest-C3EODOee.mjs.map +1 -0
  10. package/dist/{DurableAgentRuntime-BAmSkWYr.mjs → DurableAgentRuntime-CPlw5_7e.mjs} +1557 -1458
  11. package/dist/DurableAgentRuntime-CPlw5_7e.mjs.map +1 -0
  12. package/dist/{EventSource-BwliwQz9.d.mts → EventSource-BTu68yDW.d.mts} +2 -2
  13. package/dist/InMemory.d.mts +2 -2
  14. package/dist/{MessageDelivery-B943ebI_.d.mts → MessageDelivery-DjAZJ4Fd.d.mts} +42 -42
  15. package/dist/{Messaging-C_6OOg_s.d.mts → Messaging-SVu6rWV8.d.mts} +12 -6
  16. package/dist/{MessagingHost-B8oKM7AC.d.mts → MessagingHost-C6SOAcoG.d.mts} +2 -2
  17. package/dist/{MessagingHost-CRUK4JBi.mjs → MessagingHost-CZCyrnvJ.mjs} +27 -26
  18. package/dist/MessagingHost-CZCyrnvJ.mjs.map +1 -0
  19. package/dist/{Output-Dsx6k52t.d.mts → Output-CRamN1Fy.d.mts} +2 -2
  20. package/dist/{PreparedInputAdmission-CxSJSiTl.d.mts → PreparedInputAdmission-wgY4JMZY.d.mts} +3 -3
  21. package/dist/{Records-BiuJDWNe.mjs → Records-DzL4Kbtw.mjs} +2 -2
  22. package/dist/{Records-BiuJDWNe.mjs.map → Records-DzL4Kbtw.mjs.map} +1 -1
  23. package/dist/{RunContinuation-FHM7XPjq.d.mts → RunContinuation-3jB5pULC.d.mts} +13 -7
  24. package/dist/{RunContinuation-DHYNAQFi.mjs → RunContinuation-BiyKJK32.mjs} +173 -82
  25. package/dist/RunContinuation-BiyKJK32.mjs.map +1 -0
  26. package/dist/{RunJournal-kfKVxJj7.mjs → RunJournal-CPMKcfd0.mjs} +106 -73
  27. package/dist/RunJournal-CPMKcfd0.mjs.map +1 -0
  28. package/dist/{RunOptions-D8zxFIrZ.d.mts → RunOptions-Be77lKVZ.d.mts} +2 -2
  29. package/dist/{RunStorage-uqPxps09.mjs → RunStorage-CzutHJBE.mjs} +10 -6
  30. package/dist/RunStorage-CzutHJBE.mjs.map +1 -0
  31. package/dist/{Scheduling-CVyfXlBR.mjs → Scheduling-07_dsGED.mjs} +12 -12
  32. package/dist/Scheduling-07_dsGED.mjs.map +1 -0
  33. package/dist/{Subagent-vpffemql.mjs → Subagent-CexndZSp.mjs} +53 -40
  34. package/dist/Subagent-CexndZSp.mjs.map +1 -0
  35. package/dist/{Subagent-E6tvd7-k.d.mts → Subagent-xhN_aD1r.d.mts} +20 -9
  36. package/dist/{SubagentHost-B6w2m-SI.d.mts → SubagentHost-D1xBgn9g.d.mts} +2 -2
  37. package/dist/{Subscription-DONig54Q.d.mts → Subscription-DmIHlOJK.d.mts} +2 -2
  38. package/dist/{SubscriptionInput-mJIRxWJM.d.mts → SubscriptionInput-DgcQhvbe.d.mts} +2 -2
  39. package/dist/{Thread-dk-QiNml.d.mts → Thread-DzdJAY4J.d.mts} +4 -3
  40. package/dist/{ThreadHistory-ByxS8HU1.d.mts → ThreadHistory-KPPBJLJb.d.mts} +12 -2
  41. package/dist/{ThreadImport-DckUMEqJ.mjs → ThreadImport-CjlG57vH.mjs} +9 -9
  42. package/dist/ThreadImport-CjlG57vH.mjs.map +1 -0
  43. package/dist/{ThreadImport-DpHxjBrA.d.mts → ThreadImport-DP-vOk2F.d.mts} +5 -5
  44. package/dist/{ThreadStore-CEekRa80.mjs → ThreadStore-EuUilthA.mjs} +162 -85
  45. package/dist/ThreadStore-EuUilthA.mjs.map +1 -0
  46. package/dist/{ThreadTransfer-B4gTQLIl.mjs → ThreadTransfer-DB_zsrtJ.mjs} +4 -4
  47. package/dist/{ThreadTransfer-B4gTQLIl.mjs.map → ThreadTransfer-DB_zsrtJ.mjs.map} +1 -1
  48. package/dist/{WorkerAdmission-DFVbbVh2.mjs → WorkerAdmission-eBeXt_mu.mjs} +5 -5
  49. package/dist/{WorkerAdmission-DFVbbVh2.mjs.map → WorkerAdmission-eBeXt_mu.mjs.map} +1 -1
  50. package/dist/{agent-registration-ByMZ_cK5.mjs → agent-registration-a6QYaG3h.mjs} +70 -57
  51. package/dist/agent-registration-a6QYaG3h.mjs.map +1 -0
  52. package/dist/capabilities/Approval.mjs +2 -2
  53. package/dist/capabilities/Approval.mjs.map +1 -1
  54. package/dist/capabilities/BrowserUse.d.mts +102 -1867
  55. package/dist/capabilities/BrowserUse.mjs +74 -798
  56. package/dist/capabilities/BrowserUse.mjs.map +1 -1
  57. package/dist/capabilities/Budget.mjs +3 -1
  58. package/dist/capabilities/Budget.mjs.map +1 -1
  59. package/dist/capabilities/CodeMode.d.mts +1 -1
  60. package/dist/capabilities/CodeMode.mjs +16 -9
  61. package/dist/capabilities/CodeMode.mjs.map +1 -1
  62. package/dist/capabilities/Commands.d.mts +1 -1
  63. package/dist/capabilities/McpClient.mjs +1 -1
  64. package/dist/capabilities/McpClient.mjs.map +1 -1
  65. package/dist/capabilities/Messaging.d.mts +1 -1
  66. package/dist/capabilities/Messaging.mjs +5 -5
  67. package/dist/capabilities/Messaging.mjs.map +1 -1
  68. package/dist/capabilities/ModelContext.d.mts +1 -1
  69. package/dist/capabilities/ModelContext.mjs +7 -5
  70. package/dist/capabilities/ModelContext.mjs.map +1 -1
  71. package/dist/capabilities/Redaction.mjs +49 -19
  72. package/dist/capabilities/Redaction.mjs.map +1 -1
  73. package/dist/capabilities/RunHooks.d.mts +2 -2
  74. package/dist/capabilities/RunHooks.mjs +1 -1
  75. package/dist/capabilities/RunHooks.mjs.map +1 -1
  76. package/dist/capabilities/Subagent.d.mts +1 -1
  77. package/dist/capabilities/Subagent.mjs +1 -1
  78. package/dist/capabilities/SubagentReservations.mjs +3 -1
  79. package/dist/capabilities/SubagentReservations.mjs.map +1 -1
  80. package/dist/capabilities/WebCapture.mjs.map +1 -1
  81. package/dist/capabilities/WebSearch.mjs +7 -7
  82. package/dist/capabilities/WebSearch.mjs.map +1 -1
  83. package/dist/core/Agent.d.mts +1 -1
  84. package/dist/core/Agent.mjs +1 -1
  85. package/dist/core/AgentUpdates.mjs +8 -8
  86. package/dist/core/AgentUpdates.mjs.map +1 -1
  87. package/dist/core/Thread.d.mts +2 -2
  88. package/dist/core/Thread.mjs +6 -4
  89. package/dist/core/Thread.mjs.map +1 -1
  90. package/dist/core/Usage.mjs +2 -2
  91. package/dist/core/Usage.mjs.map +1 -1
  92. package/dist/durable/ActivityStore.mjs +1 -1
  93. package/dist/durable/Admin.d.mts +1 -1
  94. package/dist/durable/Admin.mjs +1 -1
  95. package/dist/durable/AgentRegistration.d.mts +1 -1
  96. package/dist/durable/AgentRegistration.mjs +1 -1
  97. package/dist/durable/Certification.d.mts +1 -1
  98. package/dist/durable/CommittedActivity.d.mts +1 -1
  99. package/dist/durable/CommittedActivity.mjs +3 -3
  100. package/dist/durable/Digest.mjs +1 -1
  101. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  102. package/dist/durable/DurableAgentRuntime.mjs +2 -2
  103. package/dist/durable/EventSource.d.mts +1 -1
  104. package/dist/durable/EventSource.mjs +7 -7
  105. package/dist/durable/EventSource.mjs.map +1 -1
  106. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  107. package/dist/durable/LifecyclePublication.d.mts +1 -1
  108. package/dist/durable/LifecyclePublication.mjs +2 -2
  109. package/dist/durable/MessageDelivery.d.mts +1 -1
  110. package/dist/durable/MessageDelivery.mjs +2 -2
  111. package/dist/durable/MessageDeliveryStoreConformance.d.mts +40 -40
  112. package/dist/durable/MessageDeliveryStoreConformance.mjs +2 -2
  113. package/dist/durable/PersistentHistory.d.mts +2 -2
  114. package/dist/durable/PersistentHistory.mjs +4 -4
  115. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  116. package/dist/durable/RecordFormat.mjs +3 -3
  117. package/dist/durable/RecordFormat.mjs.map +1 -1
  118. package/dist/durable/Records.mjs +1 -1
  119. package/dist/durable/Recovery.d.mts +1 -1
  120. package/dist/durable/Recovery.mjs +1 -1
  121. package/dist/durable/RunContinuation.d.mts +2 -2
  122. package/dist/durable/RunContinuation.mjs +3 -3
  123. package/dist/durable/RunJournal.d.mts +2 -2
  124. package/dist/durable/RunJournal.mjs +2 -2
  125. package/dist/durable/RunStorage.d.mts +1 -1
  126. package/dist/durable/RunStorage.mjs +1 -1
  127. package/dist/durable/Schedule.mjs +1 -1
  128. package/dist/durable/ScheduleStoreConformance.mjs +1 -1
  129. package/dist/durable/ScheduleTransition.mjs +1 -1
  130. package/dist/durable/Scheduling.d.mts +1 -1
  131. package/dist/durable/Scheduling.mjs +1 -1
  132. package/dist/durable/SettlementPublisher.d.mts +1 -1
  133. package/dist/durable/SettlementPublisher.mjs +3 -3
  134. package/dist/durable/SqlMemoryStore.mjs +5 -3
  135. package/dist/durable/SqlMemoryStore.mjs.map +1 -1
  136. package/dist/durable/SubmissionLedger.d.mts +1 -1
  137. package/dist/durable/SubmissionLedger.mjs +11 -3
  138. package/dist/durable/SubmissionLedger.mjs.map +1 -1
  139. package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
  140. package/dist/durable/SubmissionLedgerConformance.mjs +6 -6
  141. package/dist/durable/SubmissionStatus.d.mts +1 -1
  142. package/dist/durable/Subscription.d.mts +1 -1
  143. package/dist/durable/Subscription.mjs +2 -2
  144. package/dist/durable/SubscriptionInput.d.mts +1 -1
  145. package/dist/durable/SubscriptionInput.mjs +4 -4
  146. package/dist/durable/SubscriptionInput.mjs.map +1 -1
  147. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  148. package/dist/durable/SubscriptionStoreConformance.mjs +3 -3
  149. package/dist/durable/SubscriptionStoreConformance.mjs.map +1 -1
  150. package/dist/durable/SubscriptionTools.d.mts +2 -2
  151. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  152. package/dist/durable/SubscriptionTransition.mjs +1 -1
  153. package/dist/durable/Subscriptions.d.mts +5 -5
  154. package/dist/durable/Subscriptions.mjs +3 -3
  155. package/dist/durable/Subscriptions.mjs.map +1 -1
  156. package/dist/durable/ThreadArchiveRange.d.mts +1 -1
  157. package/dist/durable/ThreadArchiveRange.mjs +1 -1
  158. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  159. package/dist/durable/ThreadContextHistory.mjs +2 -2
  160. package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
  161. package/dist/durable/ThreadImport.d.mts +1 -1
  162. package/dist/durable/ThreadImport.mjs +1 -1
  163. package/dist/durable/ThreadInvariants.d.mts +3 -3
  164. package/dist/durable/ThreadInvariants.mjs +5 -5
  165. package/dist/durable/ThreadInvariants.mjs.map +1 -1
  166. package/dist/durable/ThreadProjection.mjs +1 -1
  167. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  168. package/dist/durable/ThreadReader.d.mts +1 -1
  169. package/dist/durable/ThreadReader.mjs +1 -1
  170. package/dist/durable/ThreadStore.d.mts +1 -1
  171. package/dist/durable/ThreadStore.mjs +2 -2
  172. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  173. package/dist/durable/ThreadStoreConformance.mjs +4 -4
  174. package/dist/durable/ThreadTransfer.d.mts +1 -1
  175. package/dist/durable/ThreadTransfer.mjs +1 -1
  176. package/dist/durable/ThreadWork.d.mts +2 -2
  177. package/dist/durable/ThreadWork.mjs +12 -6
  178. package/dist/durable/ThreadWork.mjs.map +1 -1
  179. package/dist/durable/ToolReconciler.mjs +1 -1
  180. package/dist/durable/WakeScheduler.mjs +23 -39
  181. package/dist/durable/WakeScheduler.mjs.map +1 -1
  182. package/dist/durable/WorkerAdmission.d.mts +1 -1
  183. package/dist/durable/WorkerAdmission.mjs +1 -1
  184. package/dist/durable/WorkerHost.d.mts +2 -2
  185. package/dist/engine/AgentRuntime.d.mts +1 -1
  186. package/dist/engine/AgentRuntime.mjs +1 -1
  187. package/dist/engine/MessagingHost.d.mts +1 -1
  188. package/dist/engine/MessagingHost.mjs +1 -1
  189. package/dist/engine/Output.d.mts +1 -1
  190. package/dist/engine/Output.mjs +1 -1
  191. package/dist/engine/RunOptions.d.mts +1 -1
  192. package/dist/engine/SubagentHost.d.mts +1 -1
  193. package/dist/engine/ThreadHistory.d.mts +1 -1
  194. package/dist/engine/ThreadHistory.mjs +3 -2
  195. package/dist/engine/ThreadHistory.mjs.map +1 -1
  196. package/dist/index.d.mts +21 -21
  197. package/dist/index.mjs +16 -16
  198. package/dist/{output-contract-CMIhu4Cw.mjs → output-contract-DCnLYCdp.mjs} +12 -15
  199. package/dist/{output-contract-CMIhu4Cw.mjs.map → output-contract-DCnLYCdp.mjs.map} +1 -1
  200. package/dist/{run-context-reader-OcsZr4bk.d.mts → run-context-reader-DVhJZ1Qe.d.mts} +2 -2
  201. package/package.json +1 -1
  202. package/src/capabilities/Approval.ts +58 -60
  203. package/src/capabilities/BrowserUse.ts +89 -944
  204. package/src/capabilities/Budget.ts +8 -1
  205. package/src/capabilities/CodeMode.ts +57 -33
  206. package/src/capabilities/McpClient.ts +2 -2
  207. package/src/capabilities/Messaging.ts +51 -55
  208. package/src/capabilities/ModelContext.ts +5 -2
  209. package/src/capabilities/Redaction.ts +64 -34
  210. package/src/capabilities/RunHooks.ts +52 -55
  211. package/src/capabilities/Subagent.ts +33 -33
  212. package/src/capabilities/SubagentReservations.ts +9 -1
  213. package/src/capabilities/WebCapture.ts +6 -0
  214. package/src/capabilities/WebSearch.ts +10 -7
  215. package/src/capabilities/internal/subagent-background.ts +202 -158
  216. package/src/core/Agent.ts +11 -6
  217. package/src/core/AgentUpdates.ts +8 -8
  218. package/src/core/Thread.ts +142 -146
  219. package/src/core/Usage.ts +7 -8
  220. package/src/core/internal/agent-updates.ts +1 -0
  221. package/src/durable/DurableAgentRuntime.ts +1024 -829
  222. package/src/durable/EventSource.ts +15 -9
  223. package/src/durable/RecordFormat.ts +5 -3
  224. package/src/durable/RunContinuation.ts +316 -113
  225. package/src/durable/RunJournal.ts +106 -114
  226. package/src/durable/RunStorage.ts +129 -124
  227. package/src/durable/Scheduling.ts +639 -651
  228. package/src/durable/SqlMemoryStore.ts +8 -6
  229. package/src/durable/SubmissionLedger.ts +11 -0
  230. package/src/durable/SubscriptionInput.ts +5 -3
  231. package/src/durable/SubscriptionStoreConformance.ts +23 -23
  232. package/src/durable/Subscriptions.ts +18 -19
  233. package/src/durable/ThreadImport.ts +18 -19
  234. package/src/durable/ThreadInvariants.ts +2 -1
  235. package/src/durable/ThreadStore.ts +217 -111
  236. package/src/durable/ThreadWork.ts +11 -2
  237. package/src/durable/WakeScheduler.ts +34 -66
  238. package/src/durable/internal/StorageOwner.ts +10 -4
  239. package/src/durable/internal/agent-registration.ts +96 -89
  240. package/src/durable/internal/canonical-json.ts +61 -20
  241. package/src/durable/internal/prompt-history-cache.ts +13 -4
  242. package/src/durable/internal/record-encoding.ts +120 -64
  243. package/src/durable/internal/schedule-time.ts +6 -6
  244. package/src/durable/internal/usage-totals.ts +35 -0
  245. package/src/durable/internal/worker-host.ts +69 -74
  246. package/src/engine/MessagingHost.ts +4 -2
  247. package/src/engine/ThreadHistory.ts +47 -48
  248. package/src/engine/internal/agent-runtime.ts +1298 -1217
  249. package/src/engine/internal/output-contract.ts +15 -15
  250. package/src/engine/internal/primitive-delta.ts +2 -3
  251. package/src/engine/internal/provisional-text.ts +12 -10
  252. package/src/engine/internal/tool-exposure.ts +22 -18
  253. package/src/engine/internal/tool-telemetry.ts +15 -12
  254. package/dist/Agent-0WvBWWbM.mjs.map +0 -1
  255. package/dist/AgentRuntime-CQ-qcETl.mjs.map +0 -1
  256. package/dist/Digest-DCuPlhNk.mjs.map +0 -1
  257. package/dist/DurableAgentRuntime-BAmSkWYr.mjs.map +0 -1
  258. package/dist/MessagingHost-CRUK4JBi.mjs.map +0 -1
  259. package/dist/RunContinuation-DHYNAQFi.mjs.map +0 -1
  260. package/dist/RunJournal-kfKVxJj7.mjs.map +0 -1
  261. package/dist/RunStorage-uqPxps09.mjs.map +0 -1
  262. package/dist/Scheduling-CVyfXlBR.mjs.map +0 -1
  263. package/dist/Subagent-vpffemql.mjs.map +0 -1
  264. package/dist/ThreadImport-DckUMEqJ.mjs.map +0 -1
  265. package/dist/ThreadStore-CEekRa80.mjs.map +0 -1
  266. package/dist/agent-registration-ByMZ_cK5.mjs.map +0 -1
@@ -197,766 +197,754 @@ const resolveInput = Effect.fnUntraced(function* <InputSchema extends Schema.Top
197
197
  return { payload, digest };
198
198
  });
199
199
 
200
- const makeManagement = (limits: SchedulingLimits) =>
201
- Effect.gen(function* () {
202
- const nowMillis = yield* currentMillis;
200
+ const makeManagement = Effect.fnUntraced(function* (limits: SchedulingLimits) {
201
+ const nowMillis = yield* currentMillis;
203
202
 
204
- yield* Schema.decodeEffect(ScheduleInstant)(nowMillis + limits.recoveryPollMillis).pipe(
205
- Effect.mapError(() =>
206
- ScheduleValidationError.make({
207
- message: "Scheduling recovery deadline exceeds the supported instant range",
208
- }),
209
- ),
203
+ yield* Schema.decodeEffect(ScheduleInstant)(nowMillis + limits.recoveryPollMillis).pipe(
204
+ Effect.mapError(() =>
205
+ ScheduleValidationError.make({
206
+ message: "Scheduling recovery deadline exceeds the supported instant range",
207
+ }),
208
+ ),
209
+ );
210
+ const store = yield* ScheduleStore;
211
+ const authorizer = yield* ScheduleAuthorizer;
212
+ const wake = yield* ScheduleWake;
213
+ const crypto = yield* Crypto.Crypto;
214
+
215
+ const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>) =>
216
+ Effect.provideService(effect, Crypto.Crypto, crypto);
217
+
218
+ const fingerprint = Effect.fnUntraced(function* (
219
+ agentId: AgentId,
220
+ inputDigest: Digest,
221
+ options: ScheduleCreateOptions,
222
+ ) {
223
+ const timing = yield* Schema.encodeEffect(ScheduleTimingRequestSchema)(options.timing).pipe(
224
+ Effect.mapError(() => ScheduleValidationError.make({ message: "Invalid timing request" })),
210
225
  );
211
- const store = yield* ScheduleStore;
212
- const authorizer = yield* ScheduleAuthorizer;
213
- const wake = yield* ScheduleWake;
214
- const crypto = yield* Crypto.Crypto;
215
226
 
216
- const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>) =>
217
- Effect.provideService(effect, Crypto.Crypto, crypto);
227
+ const destination = yield* Schema.encodeEffect(ScheduleDestinationSchema)(
228
+ options.destination,
229
+ ).pipe(Effect.mapError(() => ScheduleValidationError.make({ message: "Invalid destination" })));
218
230
 
219
- const fingerprint = Effect.fnUntraced(function* (
220
- agentId: AgentId,
221
- inputDigest: Digest,
222
- options: ScheduleCreateOptions,
223
- ) {
224
- const timing = yield* Schema.encodeEffect(ScheduleTimingRequestSchema)(options.timing).pipe(
225
- Effect.mapError(() => ScheduleValidationError.make({ message: "Invalid timing request" })),
226
- );
227
-
228
- const destination = yield* Schema.encodeEffect(ScheduleDestinationSchema)(
229
- options.destination,
230
- ).pipe(
231
- Effect.mapError(() => ScheduleValidationError.make({ message: "Invalid destination" })),
232
- );
231
+ const definitions = yield* Schema.encodeEffect(DefinitionDigests)(options.definitions).pipe(
232
+ Effect.mapError(() =>
233
+ ScheduleValidationError.make({ message: "Invalid definition digests" }),
234
+ ),
235
+ );
233
236
 
234
- const definitions = yield* Schema.encodeEffect(DefinitionDigests)(options.definitions).pipe(
235
- Effect.mapError(() =>
236
- ScheduleValidationError.make({ message: "Invalid definition digests" }),
237
- ),
238
- );
237
+ return yield* withCrypto(
238
+ digestJson({
239
+ schemaVersion: 1,
240
+ owner: options.scope.owner,
241
+ scheduleId: options.scheduleId,
242
+ registeringPrincipal: options.scope.principal,
243
+ deliveryPrincipal: options.deliveryPrincipal,
244
+ ...(options.admissionGroup === undefined ? {} : { admissionGroup: options.admissionGroup }),
245
+ ...(options.admissionFence === undefined ? {} : { admissionFence: options.admissionFence }),
246
+ agentId,
247
+ definitions,
248
+ destination,
249
+ timing,
250
+ inputDigest,
251
+ }),
252
+ ).pipe(
253
+ Effect.mapError(() =>
254
+ ScheduleValidationError.make({ message: "Unable to digest Schedule registration" }),
255
+ ),
256
+ );
257
+ });
239
258
 
240
- return yield* withCrypto(
241
- digestJson({
242
- schemaVersion: 1,
243
- owner: options.scope.owner,
244
- scheduleId: options.scheduleId,
245
- registeringPrincipal: options.scope.principal,
246
- deliveryPrincipal: options.deliveryPrincipal,
247
- ...(options.admissionGroup === undefined
248
- ? {}
249
- : { admissionGroup: options.admissionGroup }),
250
- ...(options.admissionFence === undefined
251
- ? {}
252
- : { admissionFence: options.admissionFence }),
253
- agentId,
254
- definitions,
255
- destination,
256
- timing,
257
- inputDigest,
258
- }),
259
- ).pipe(
260
- Effect.mapError(() =>
261
- ScheduleValidationError.make({ message: "Unable to digest Schedule registration" }),
262
- ),
263
- );
264
- });
259
+ const configuration = (
260
+ agentId: AgentId,
261
+ payload: PersistedJson,
262
+ inputDigest: Digest,
263
+ options: ScheduleCreateOptions,
264
+ timing: ScheduleConfiguration["timing"],
265
+ ): ScheduleConfiguration => ({
266
+ timing,
267
+ destination: options.destination,
268
+ deliveryPrincipal: options.deliveryPrincipal,
269
+ ...(options.admissionGroup === undefined ? {} : { admissionGroup: options.admissionGroup }),
270
+ ...(options.admissionFence === undefined ? {} : { admissionFence: options.admissionFence }),
271
+ agentId,
272
+ definitions: options.definitions,
273
+ input: payload,
274
+ inputDigest,
275
+ });
265
276
 
266
- const configuration = (
267
- agentId: AgentId,
268
- payload: PersistedJson,
269
- inputDigest: Digest,
270
- options: ScheduleCreateOptions,
271
- timing: ScheduleConfiguration["timing"],
272
- ): ScheduleConfiguration => ({
273
- timing,
274
- destination: options.destination,
275
- deliveryPrincipal: options.deliveryPrincipal,
276
- ...(options.admissionGroup === undefined ? {} : { admissionGroup: options.admissionGroup }),
277
- ...(options.admissionFence === undefined ? {} : { admissionFence: options.admissionFence }),
278
- agentId,
279
- definitions: options.definitions,
280
- input: payload,
281
- inputDigest,
282
- });
277
+ const create = Effect.fnUntraced(function* <InputSchema extends Schema.Top>(
278
+ agent: DurableSubmitAgent<InputSchema>,
279
+ input: InputSchema["Type"],
280
+ options: ScheduleCreateOptions,
281
+ ): Effect.fn.Return<
282
+ ScheduleSnapshot,
283
+ ScheduleManagementFailure,
284
+ InputSchema["EncodingServices"]
285
+ > {
286
+ const nowMillis = yield* currentMillis;
283
287
 
284
- const create = Effect.fnUntraced(function* <InputSchema extends Schema.Top>(
285
- agent: DurableSubmitAgent<InputSchema>,
286
- input: InputSchema["Type"],
287
- options: ScheduleCreateOptions,
288
- ): Effect.fn.Return<
289
- ScheduleSnapshot,
290
- ScheduleManagementFailure,
291
- InputSchema["EncodingServices"]
292
- > {
293
- const nowMillis = yield* currentMillis;
288
+ const resolvedInput = yield* resolveInput(agent, input, limits.maxInputBytes).pipe(
289
+ Effect.provideService(Crypto.Crypto, crypto),
290
+ );
294
291
 
295
- const resolvedInput = yield* resolveInput(agent, input, limits.maxInputBytes).pipe(
296
- Effect.provideService(Crypto.Crypto, crypto),
297
- );
292
+ const creationFingerprint = yield* fingerprint(
293
+ agent.definition.id,
294
+ resolvedInput.digest,
295
+ options,
296
+ );
298
297
 
299
- const creationFingerprint = yield* fingerprint(
300
- agent.definition.id,
301
- resolvedInput.digest,
302
- options,
303
- );
298
+ const key = keyOf(options.scope, options.scheduleId);
304
299
 
305
- const key = keyOf(options.scope, options.scheduleId);
300
+ yield* authorizer.manage({
301
+ operation: "create",
302
+ scope: options.scope,
303
+ scheduleId: options.scheduleId,
304
+ });
305
+ const existing = yield* store.get(key);
306
306
 
307
+ if (existing !== null) {
308
+ if (existing.creationFingerprint !== creationFingerprint) {
309
+ return yield* ScheduleConflict.make({ reason: "creation", key });
310
+ }
307
311
  yield* authorizer.manage({
308
312
  operation: "create",
309
313
  scope: options.scope,
310
314
  scheduleId: options.scheduleId,
315
+ configuration: existing.configuration,
311
316
  });
312
- const existing = yield* store.get(key);
313
-
314
- if (existing !== null) {
315
- if (existing.creationFingerprint !== creationFingerprint) {
316
- return yield* ScheduleConflict.make({ reason: "creation", key });
317
- }
318
- yield* authorizer.manage({
319
- operation: "create",
320
- scope: options.scope,
321
- scheduleId: options.scheduleId,
322
- configuration: existing.configuration,
323
- });
324
-
325
- return asSnapshot(existing, nowMillis);
326
- }
327
317
 
328
- const timing = yield* Effect.fromResult(
329
- normalizeScheduleTiming(options.timing, nowMillis, limits.minIntervalMillis),
330
- );
318
+ return asSnapshot(existing, nowMillis);
319
+ }
331
320
 
332
- const nextAtMillis = yield* Effect.fromResult(scheduleInitialCursor(timing, nowMillis));
321
+ const timing = yield* Effect.fromResult(
322
+ normalizeScheduleTiming(options.timing, nowMillis, limits.minIntervalMillis),
323
+ );
333
324
 
334
- if (nextAtMillis === null) {
335
- return yield* ScheduleValidationError.make({ message: "Timing has no next occurrence" });
336
- }
325
+ const nextAtMillis = yield* Effect.fromResult(scheduleInitialCursor(timing, nowMillis));
337
326
 
338
- const nextConfiguration = configuration(
339
- agent.definition.id,
340
- resolvedInput.payload,
341
- resolvedInput.digest,
342
- options,
343
- timing,
344
- );
327
+ if (nextAtMillis === null) {
328
+ return yield* ScheduleValidationError.make({ message: "Timing has no next occurrence" });
329
+ }
345
330
 
346
- yield* authorizer.manage({
347
- operation: "create",
348
- scope: options.scope,
349
- scheduleId: options.scheduleId,
350
- configuration: nextConfiguration,
351
- });
331
+ const nextConfiguration = configuration(
332
+ agent.definition.id,
333
+ resolvedInput.payload,
334
+ resolvedInput.digest,
335
+ options,
336
+ timing,
337
+ );
352
338
 
353
- const record: ScheduleRecord = {
354
- schemaVersion: 1,
355
- owner: options.scope.owner,
356
- scheduleId: options.scheduleId,
357
- creationFingerprint,
358
- createdBy: options.scope.principal,
359
- createdAtMillis: nowMillis,
360
- updatedAtMillis: nowMillis,
361
- configurationRevision: 1,
362
- version: 1,
363
- configuration: nextConfiguration,
364
- state: "active",
365
- nextAtMillis,
366
- pending: null,
367
- lastReceipt: null,
368
- lastRefusal: null,
369
- lastSkippedRange: null,
370
- };
339
+ yield* authorizer.manage({
340
+ operation: "create",
341
+ scope: options.scope,
342
+ scheduleId: options.scheduleId,
343
+ configuration: nextConfiguration,
344
+ });
371
345
 
372
- const inserted = yield* store.insert(record, limits.maxSchedulesPerOwner);
346
+ const record: ScheduleRecord = {
347
+ schemaVersion: 1,
348
+ owner: options.scope.owner,
349
+ scheduleId: options.scheduleId,
350
+ creationFingerprint,
351
+ createdBy: options.scope.principal,
352
+ createdAtMillis: nowMillis,
353
+ updatedAtMillis: nowMillis,
354
+ configurationRevision: 1,
355
+ version: 1,
356
+ configuration: nextConfiguration,
357
+ state: "active",
358
+ nextAtMillis,
359
+ pending: null,
360
+ lastReceipt: null,
361
+ lastRefusal: null,
362
+ lastSkippedRange: null,
363
+ };
364
+
365
+ const inserted = yield* store.insert(record, limits.maxSchedulesPerOwner);
366
+
367
+ yield* wake.notify;
368
+
369
+ return asSnapshot(inserted, nowMillis);
370
+ });
373
371
 
374
- yield* wake.notify;
372
+ const update = Effect.fnUntraced(function* <InputSchema extends Schema.Top>(
373
+ agent: DurableSubmitAgent<InputSchema>,
374
+ input: InputSchema["Type"],
375
+ options: ScheduleUpdateOptions,
376
+ ): Effect.fn.Return<
377
+ ScheduleSnapshot,
378
+ ScheduleManagementFailure,
379
+ InputSchema["EncodingServices"]
380
+ > {
381
+ const nowMillis = yield* currentMillis;
382
+ const key = keyOf(options.scope, options.scheduleId);
375
383
 
376
- return asSnapshot(inserted, nowMillis);
384
+ yield* authorizer.manage({
385
+ operation: "update",
386
+ scope: options.scope,
387
+ scheduleId: options.scheduleId,
377
388
  });
389
+ yield* validateRequestedFields(options);
390
+ const existing = yield* store.get(key);
378
391
 
379
- const update = Effect.fnUntraced(function* <InputSchema extends Schema.Top>(
380
- agent: DurableSubmitAgent<InputSchema>,
381
- input: InputSchema["Type"],
382
- options: ScheduleUpdateOptions,
383
- ): Effect.fn.Return<
384
- ScheduleSnapshot,
385
- ScheduleManagementFailure,
386
- InputSchema["EncodingServices"]
387
- > {
388
- const nowMillis = yield* currentMillis;
389
- const key = keyOf(options.scope, options.scheduleId);
390
-
391
- yield* authorizer.manage({
392
- operation: "update",
393
- scope: options.scope,
394
- scheduleId: options.scheduleId,
395
- });
396
- yield* validateRequestedFields(options);
397
- const existing = yield* store.get(key);
392
+ if (existing === null) return yield* notFound(key);
393
+ if (existing.configurationRevision !== options.expectedRevision) {
394
+ return yield* ScheduleConflict.make({ reason: "revision", key });
395
+ }
398
396
 
399
- if (existing === null) return yield* notFound(key);
400
- if (existing.configurationRevision !== options.expectedRevision) {
401
- return yield* ScheduleConflict.make({ reason: "revision", key });
402
- }
397
+ const resolvedInput = yield* resolveInput(agent, input, limits.maxInputBytes).pipe(
398
+ Effect.provideService(Crypto.Crypto, crypto),
399
+ );
403
400
 
404
- const resolvedInput = yield* resolveInput(agent, input, limits.maxInputBytes).pipe(
405
- Effect.provideService(Crypto.Crypto, crypto),
406
- );
401
+ const timingRequest =
402
+ options.timing._tag === "Interval" &&
403
+ options.timing.anchorMillis === undefined &&
404
+ existing.configuration.timing._tag === "Interval" &&
405
+ existing.configuration.timing.everyMillis === options.timing.everyMillis
406
+ ? { ...options.timing, anchorMillis: existing.configuration.timing.anchorMillis }
407
+ : options.timing;
407
408
 
408
- const timingRequest =
409
- options.timing._tag === "Interval" &&
410
- options.timing.anchorMillis === undefined &&
411
- existing.configuration.timing._tag === "Interval" &&
412
- existing.configuration.timing.everyMillis === options.timing.everyMillis
413
- ? { ...options.timing, anchorMillis: existing.configuration.timing.anchorMillis }
414
- : options.timing;
409
+ const timing = yield* Effect.fromResult(
410
+ normalizeScheduleTiming(timingRequest, nowMillis, limits.minIntervalMillis),
411
+ );
415
412
 
416
- const timing = yield* Effect.fromResult(
417
- normalizeScheduleTiming(timingRequest, nowMillis, limits.minIntervalMillis),
418
- );
413
+ const nextAtMillis =
414
+ timing._tag === "At"
415
+ ? timing.atMillis
416
+ : yield* Effect.fromResult(scheduleNextAfter(timing, nowMillis));
419
417
 
420
- const nextAtMillis =
421
- timing._tag === "At"
422
- ? timing.atMillis
423
- : yield* Effect.fromResult(scheduleNextAfter(timing, nowMillis));
418
+ if (nextAtMillis === null) {
419
+ return yield* ScheduleValidationError.make({ message: "Timing has no next occurrence" });
420
+ }
424
421
 
425
- if (nextAtMillis === null) {
426
- return yield* ScheduleValidationError.make({ message: "Timing has no next occurrence" });
427
- }
422
+ const nextConfiguration = configuration(
423
+ agent.definition.id,
424
+ resolvedInput.payload,
425
+ resolvedInput.digest,
426
+ options,
427
+ timing,
428
+ );
428
429
 
429
- const nextConfiguration = configuration(
430
- agent.definition.id,
431
- resolvedInput.payload,
432
- resolvedInput.digest,
433
- options,
434
- timing,
435
- );
430
+ yield* authorizer.manage({
431
+ operation: "update",
432
+ scope: options.scope,
433
+ scheduleId: options.scheduleId,
434
+ configuration: nextConfiguration,
435
+ });
436
436
 
437
- yield* authorizer.manage({
438
- operation: "update",
439
- scope: options.scope,
440
- scheduleId: options.scheduleId,
437
+ const changed = yield* store.change(
438
+ key,
439
+ {
440
+ _tag: "Update",
441
+ expectedRevision: options.expectedRevision,
441
442
  configuration: nextConfiguration,
442
- });
443
-
444
- const changed = yield* store.change(
445
- key,
446
- {
447
- _tag: "Update",
448
- expectedRevision: options.expectedRevision,
449
- configuration: nextConfiguration,
450
- nextAtMillis,
451
- nowMillis,
452
- },
453
- limits.maxSchedulesPerOwner,
454
- );
443
+ nextAtMillis,
444
+ nowMillis,
445
+ },
446
+ limits.maxSchedulesPerOwner,
447
+ );
455
448
 
456
- yield* wake.notify;
449
+ yield* wake.notify;
457
450
 
458
- return asSnapshot(changed, nowMillis);
459
- });
451
+ return asSnapshot(changed, nowMillis);
452
+ });
460
453
 
461
- const get = Effect.fnUntraced(function* (scope: ScheduleScope, scheduleId: ScheduleId) {
462
- yield* authorizer.manage({ operation: "get", scope, scheduleId });
463
- const key = keyOf(scope, scheduleId);
464
- const record = yield* store.get(key);
454
+ const get = Effect.fnUntraced(function* (scope: ScheduleScope, scheduleId: ScheduleId) {
455
+ yield* authorizer.manage({ operation: "get", scope, scheduleId });
456
+ const key = keyOf(scope, scheduleId);
457
+ const record = yield* store.get(key);
465
458
 
466
- if (record === null) return yield* notFound(key);
459
+ if (record === null) return yield* notFound(key);
467
460
 
468
- return asSnapshot(record, yield* currentMillis);
469
- });
461
+ return asSnapshot(record, yield* currentMillis);
462
+ });
470
463
 
471
- const list = Effect.fnUntraced(function* (
472
- scope: ScheduleScope,
473
- options: ScheduleListOptions = {},
474
- ) {
475
- yield* authorizer.manage({ operation: "list", scope });
476
- const limit = options.limit ?? 50;
464
+ const list = Effect.fnUntraced(function* (
465
+ scope: ScheduleScope,
466
+ options: ScheduleListOptions = {},
467
+ ) {
468
+ yield* authorizer.manage({ operation: "list", scope });
469
+ const limit = options.limit ?? 50;
477
470
 
478
- if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
479
- return yield* ScheduleValidationError.make({
480
- message: "Schedule page limit must be an integer from 1 through 100",
481
- });
482
- }
483
-
484
- const page = yield* store.list({
485
- owner: scope.owner,
486
- ...(options.after === undefined ? {} : { after: options.after }),
487
- limit,
471
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
472
+ return yield* ScheduleValidationError.make({
473
+ message: "Schedule page limit must be an integer from 1 through 100",
488
474
  });
475
+ }
489
476
 
490
- const nowMillis = yield* currentMillis;
491
-
492
- return { items: page.items.map((record) => asSnapshot(record, nowMillis)), next: page.next };
477
+ const page = yield* store.list({
478
+ owner: scope.owner,
479
+ ...(options.after === undefined ? {} : { after: options.after }),
480
+ limit,
493
481
  });
494
482
 
495
- const control = Effect.fnUntraced(function* (
496
- operation: "pause" | "resume" | "cancel",
497
- scope: ScheduleScope,
498
- scheduleId: ScheduleId,
499
- expectedRevision: number,
500
- ): Effect.fn.Return<ScheduleSnapshot, ScheduleManagementFailure> {
501
- yield* authorizer.manage({ operation, scope, scheduleId });
502
- const key = keyOf(scope, scheduleId);
503
-
504
- const attempt = (): Effect.Effect<ScheduleRecord, ScheduleManagementFailure> =>
505
- Effect.suspend(() =>
506
- Effect.gen(function* () {
507
- const record = yield* store.get(key);
508
-
509
- if (record === null) return yield* notFound(key);
510
- if (record.configurationRevision !== expectedRevision) {
511
- return yield* ScheduleConflict.make({ reason: "revision", key });
512
- }
513
- if (operation === "resume" && record.state === "active") return record;
514
- const nowMillis = yield* currentMillis;
515
-
516
- const nextAtMillis =
517
- operation === "resume"
518
- ? yield* Effect.fromResult(
519
- scheduleResumeCursor(
520
- record.configuration.timing,
521
- record.nextAtMillis,
522
- nowMillis,
523
- ),
524
- )
525
- : operation === "cancel"
526
- ? null
527
- : record.nextAtMillis;
528
-
529
- const skippedRange =
530
- operation === "resume" &&
531
- record.configuration.timing._tag !== "At" &&
532
- record.nextAtMillis !== null &&
533
- nextAtMillis !== null &&
534
- nextAtMillis > record.nextAtMillis
535
- ? { fromMillis: record.nextAtMillis, toMillis: nextAtMillis }
536
- : null;
537
-
538
- return yield* store
539
- .change(
540
- key,
541
- {
542
- _tag: "Control",
543
- expectedRevision,
544
- expectedVersion: record.version,
545
- action: operation,
546
- nextAtMillis,
547
- nowMillis,
548
- skippedRange,
549
- },
550
- limits.maxSchedulesPerOwner,
551
- )
552
- .pipe(
553
- Effect.catchTag("ScheduleConflict", (error) =>
554
- error.reason === "revision" ? attempt() : Effect.fail(error),
555
- ),
556
- );
557
- }),
558
- );
483
+ const nowMillis = yield* currentMillis;
559
484
 
560
- const changed = yield* attempt();
485
+ return { items: page.items.map((record) => asSnapshot(record, nowMillis)), next: page.next };
486
+ });
561
487
 
562
- yield* wake.notify;
488
+ const control = Effect.fnUntraced(function* (
489
+ operation: "pause" | "resume" | "cancel",
490
+ scope: ScheduleScope,
491
+ scheduleId: ScheduleId,
492
+ expectedRevision: number,
493
+ ): Effect.fn.Return<ScheduleSnapshot, ScheduleManagementFailure> {
494
+ yield* authorizer.manage({ operation, scope, scheduleId });
495
+ const key = keyOf(scope, scheduleId);
496
+
497
+ const attempt = (): Effect.Effect<ScheduleRecord, ScheduleManagementFailure> =>
498
+ Effect.suspend(() =>
499
+ Effect.gen(function* () {
500
+ const record = yield* store.get(key);
501
+
502
+ if (record === null) return yield* notFound(key);
503
+ if (record.configurationRevision !== expectedRevision) {
504
+ return yield* ScheduleConflict.make({ reason: "revision", key });
505
+ }
506
+ if (operation === "resume" && record.state === "active") return record;
507
+ const nowMillis = yield* currentMillis;
508
+
509
+ const nextAtMillis =
510
+ operation === "resume"
511
+ ? yield* Effect.fromResult(
512
+ scheduleResumeCursor(record.configuration.timing, record.nextAtMillis, nowMillis),
513
+ )
514
+ : operation === "cancel"
515
+ ? null
516
+ : record.nextAtMillis;
517
+
518
+ const skippedRange =
519
+ operation === "resume" &&
520
+ record.configuration.timing._tag !== "At" &&
521
+ record.nextAtMillis !== null &&
522
+ nextAtMillis !== null &&
523
+ nextAtMillis > record.nextAtMillis
524
+ ? { fromMillis: record.nextAtMillis, toMillis: nextAtMillis }
525
+ : null;
526
+
527
+ return yield* store
528
+ .change(
529
+ key,
530
+ {
531
+ _tag: "Control",
532
+ expectedRevision,
533
+ expectedVersion: record.version,
534
+ action: operation,
535
+ nextAtMillis,
536
+ nowMillis,
537
+ skippedRange,
538
+ },
539
+ limits.maxSchedulesPerOwner,
540
+ )
541
+ .pipe(
542
+ Effect.catchTag("ScheduleConflict", (error) =>
543
+ error.reason === "revision" ? attempt() : Effect.fail(error),
544
+ ),
545
+ );
546
+ }),
547
+ );
563
548
 
564
- return asSnapshot(changed, yield* currentMillis);
565
- });
549
+ const changed = yield* attempt();
566
550
 
567
- const recover = Effect.fn("Scheduling.recover")(function* (
568
- scope: ScheduleScope,
569
- scheduleId: ScheduleId,
570
- expectedRevision: number,
571
- expectedGeneration: number,
572
- ) {
573
- yield* authorizer.manage({ operation: "recover", scope, scheduleId });
574
- const key = keyOf(scope, scheduleId);
575
- const record = yield* store.get(key);
576
-
577
- if (record === null) return yield* notFound(key);
578
- if (record.configurationRevision !== expectedRevision)
579
- return yield* ScheduleConflict.make({ reason: "revision", key });
580
- const time = yield* currentMillis;
581
-
582
- if (record.pending === null) return asSnapshot(record, time);
583
-
584
- const recovered = yield* store.change(key, {
585
- _tag: "Recover",
586
- expectedGeneration,
587
- expectedRevision,
588
- occurrenceId: record.pending.envelope.occurrenceId,
589
- nowMillis: time,
590
- });
551
+ yield* wake.notify;
591
552
 
592
- yield* wake.notify;
553
+ return asSnapshot(changed, yield* currentMillis);
554
+ });
593
555
 
594
- return asSnapshot(recovered, time);
556
+ const recover = Effect.fn("Scheduling.recover")(function* (
557
+ scope: ScheduleScope,
558
+ scheduleId: ScheduleId,
559
+ expectedRevision: number,
560
+ expectedGeneration: number,
561
+ ) {
562
+ yield* authorizer.manage({ operation: "recover", scope, scheduleId });
563
+ const key = keyOf(scope, scheduleId);
564
+ const record = yield* store.get(key);
565
+
566
+ if (record === null) return yield* notFound(key);
567
+ if (record.configurationRevision !== expectedRevision)
568
+ return yield* ScheduleConflict.make({ reason: "revision", key });
569
+ const time = yield* currentMillis;
570
+
571
+ if (record.pending === null) return asSnapshot(record, time);
572
+
573
+ const recovered = yield* store.change(key, {
574
+ _tag: "Recover",
575
+ expectedGeneration,
576
+ expectedRevision,
577
+ occurrenceId: record.pending.envelope.occurrenceId,
578
+ nowMillis: time,
595
579
  });
596
580
 
597
- return Scheduling.of({
598
- recover,
599
- create,
600
- update,
601
- get,
602
- list,
603
- pause: (scope, id, revision) => control("pause", scope, id, revision),
604
- resume: (scope, id, revision) => control("resume", scope, id, revision),
605
- cancel: (scope, id, revision) => control("cancel", scope, id, revision),
606
- });
581
+ yield* wake.notify;
582
+
583
+ return asSnapshot(recovered, time);
607
584
  });
608
585
 
609
- const makeDriver = (limits: SchedulingLimits) =>
610
- Effect.gen(function* () {
611
- const store = yield* ScheduleStore;
612
- const authorizer = yield* ScheduleAuthorizer;
613
- const admission = yield* ScheduledInputAdmission;
614
- const failpoint = yield* ScheduleFailpoint;
615
- const crypto = yield* Crypto.Crypto;
616
- const admissionSemaphore = yield* Semaphore.make(limits.admissionConcurrency);
617
-
618
- const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>) =>
619
- Effect.provideService(effect, Crypto.Crypto, crypto);
620
-
621
- const retry = Effect.fnUntraced(function* (
622
- key: ScheduleKey,
623
- record: ScheduleRecord,
624
- reason: ScheduleRetryReason,
625
- nowMillis: number,
626
- ) {
627
- if (record.pending === null) return record;
628
- const current = record.pending.retry;
586
+ return Scheduling.of({
587
+ recover,
588
+ create,
589
+ update,
590
+ get,
591
+ list,
592
+ pause: (scope, id, revision) => control("pause", scope, id, revision),
593
+ resume: (scope, id, revision) => control("resume", scope, id, revision),
594
+ cancel: (scope, id, revision) => control("cancel", scope, id, revision),
595
+ });
596
+ });
629
597
 
630
- const delay = Math.min(
631
- limits.retryBaseMillis * 2 ** Math.min(current.attempts, 52),
632
- limits.retryMaxMillis,
633
- );
598
+ const makeDriver = Effect.fnUntraced(function* (limits: SchedulingLimits) {
599
+ const store = yield* ScheduleStore;
600
+ const authorizer = yield* ScheduleAuthorizer;
601
+ const admission = yield* ScheduledInputAdmission;
602
+ const failpoint = yield* ScheduleFailpoint;
603
+ const crypto = yield* Crypto.Crypto;
604
+ const admissionSemaphore = yield* Semaphore.make(limits.admissionConcurrency);
605
+
606
+ const withCrypto = <A, E>(effect: Effect.Effect<A, E, Crypto.Crypto>) =>
607
+ Effect.provideService(effect, Crypto.Crypto, crypto);
608
+
609
+ const retry = Effect.fnUntraced(function* (
610
+ key: ScheduleKey,
611
+ record: ScheduleRecord,
612
+ reason: ScheduleRetryReason,
613
+ nowMillis: number,
614
+ ) {
615
+ if (record.pending === null) return record;
616
+ const current = record.pending.retry;
617
+
618
+ const delay = Math.min(
619
+ limits.retryBaseMillis * 2 ** Math.min(current.attempts, 52),
620
+ limits.retryMaxMillis,
621
+ );
634
622
 
635
- return yield* store.change(key, {
636
- _tag: "Retry",
637
- occurrenceId: record.pending.envelope.occurrenceId,
638
- retry: {
639
- generation: current.generation,
640
- attempts: current.attempts + 1,
641
- automaticAttempts: current.automaticAttempts + 1,
642
- parked: current.automaticAttempts + 1 >= (limits.maxAutomaticAttempts ?? 8),
643
- nextAttemptAtMillis: Math.min(nowMillis + delay, 8_640_000_000_000_000),
644
- lastAttemptAtMillis: nowMillis,
645
- lastFailure: reason,
646
- },
647
- nowMillis,
648
- });
623
+ return yield* store.change(key, {
624
+ _tag: "Retry",
625
+ occurrenceId: record.pending.envelope.occurrenceId,
626
+ retry: {
627
+ generation: current.generation,
628
+ attempts: current.attempts + 1,
629
+ automaticAttempts: current.automaticAttempts + 1,
630
+ parked: current.automaticAttempts + 1 >= (limits.maxAutomaticAttempts ?? 8),
631
+ nextAttemptAtMillis: Math.min(nowMillis + delay, 8_640_000_000_000_000),
632
+ lastAttemptAtMillis: nowMillis,
633
+ lastFailure: reason,
634
+ },
635
+ nowMillis,
649
636
  });
637
+ });
650
638
 
651
- const verifyPending = Effect.fnUntraced(function* (key: ScheduleKey, record: ScheduleRecord) {
652
- const pending = record.pending;
639
+ const verifyPending = Effect.fnUntraced(function* (key: ScheduleKey, record: ScheduleRecord) {
640
+ const pending = record.pending;
653
641
 
654
- if (pending === null) return;
655
- const envelope = pending.envelope;
642
+ if (pending === null) return;
643
+ const envelope = pending.envelope;
656
644
 
657
- const occurrenceId = yield* withCrypto(
658
- digestJson({
659
- schemaVersion: 1,
660
- owner: envelope.owner,
661
- scheduleId: envelope.scheduleId,
662
- configurationRevision: envelope.configurationRevision,
663
- intendedAtMillis: envelope.intendedAtMillis,
664
- }),
665
- ).pipe(Effect.mapError(() => corrupt("process")));
666
-
667
- const usesCurrentConfiguration =
668
- envelope.configurationRevision === record.configurationRevision;
645
+ const occurrenceId = yield* withCrypto(
646
+ digestJson({
647
+ schemaVersion: 1,
648
+ owner: envelope.owner,
649
+ scheduleId: envelope.scheduleId,
650
+ configurationRevision: envelope.configurationRevision,
651
+ intendedAtMillis: envelope.intendedAtMillis,
652
+ }),
653
+ ).pipe(Effect.mapError(() => corrupt("process")));
669
654
 
670
- const expectedThreadId = usesCurrentConfiguration
671
- ? record.configuration.destination._tag === "ExistingThread"
672
- ? record.configuration.destination.threadId
673
- : Schema.decodeSync(ThreadId)(`schedule-thread:${envelope.occurrenceId}`)
674
- : null;
655
+ const usesCurrentConfiguration =
656
+ envelope.configurationRevision === record.configurationRevision;
675
657
 
676
- const expectedAdmissionKey = Schema.decodeSync(IdempotencyKey)(
677
- `schedule-occurrence:${envelope.occurrenceId}`,
678
- );
658
+ const expectedThreadId = usesCurrentConfiguration
659
+ ? record.configuration.destination._tag === "ExistingThread"
660
+ ? record.configuration.destination.threadId
661
+ : Schema.decodeSync(ThreadId)(`schedule-thread:${envelope.occurrenceId}`)
662
+ : null;
679
663
 
680
- const digest = yield* withCrypto(digestJson(envelope.input)).pipe(
681
- Effect.mapError(() => corrupt("process")),
682
- );
664
+ const expectedAdmissionKey = Schema.decodeSync(IdempotencyKey)(
665
+ `schedule-occurrence:${envelope.occurrenceId}`,
666
+ );
683
667
 
684
- if (
685
- envelope.schemaVersion !== 1 ||
686
- envelope.owner.tenantId !== key.owner.tenantId ||
687
- envelope.owner.ownerId !== key.owner.ownerId ||
688
- envelope.scheduleId !== key.scheduleId ||
689
- envelope.configurationRevision > record.configurationRevision ||
690
- envelope.occurrenceId !== occurrenceId ||
691
- (usesCurrentConfiguration &&
692
- (envelope.deliveryPrincipal !== record.configuration.deliveryPrincipal ||
693
- envelope.agentId !== record.configuration.agentId ||
694
- !equivalentDefinitions(envelope.definitions, record.configuration.definitions) ||
695
- !equivalentInput(envelope.input, record.configuration.input) ||
696
- envelope.inputDigest !== record.configuration.inputDigest)) ||
697
- envelope.inputDigest !== digest ||
698
- (expectedThreadId !== null && envelope.threadId !== expectedThreadId) ||
699
- envelope.admissionKey !== expectedAdmissionKey
700
- ) {
701
- return yield* corrupt("process");
702
- }
703
- });
668
+ const digest = yield* withCrypto(digestJson(envelope.input)).pipe(
669
+ Effect.mapError(() => corrupt("process")),
670
+ );
704
671
 
705
- const deliverPending = Effect.fnUntraced(function* (
706
- key: ScheduleKey,
707
- record: ScheduleRecord,
708
- nowMillis: number,
672
+ if (
673
+ envelope.schemaVersion !== 1 ||
674
+ envelope.owner.tenantId !== key.owner.tenantId ||
675
+ envelope.owner.ownerId !== key.owner.ownerId ||
676
+ envelope.scheduleId !== key.scheduleId ||
677
+ envelope.configurationRevision > record.configurationRevision ||
678
+ envelope.occurrenceId !== occurrenceId ||
679
+ (usesCurrentConfiguration &&
680
+ (envelope.deliveryPrincipal !== record.configuration.deliveryPrincipal ||
681
+ envelope.agentId !== record.configuration.agentId ||
682
+ !equivalentDefinitions(envelope.definitions, record.configuration.definitions) ||
683
+ !equivalentInput(envelope.input, record.configuration.input) ||
684
+ envelope.inputDigest !== record.configuration.inputDigest)) ||
685
+ envelope.inputDigest !== digest ||
686
+ (expectedThreadId !== null && envelope.threadId !== expectedThreadId) ||
687
+ envelope.admissionKey !== expectedAdmissionKey
709
688
  ) {
710
- if (
711
- record.pending === null ||
712
- record.pending.retry.parked === true ||
713
- record.pending.retry.nextAttemptAtMillis > nowMillis
714
- ) {
715
- return record;
716
- }
717
- yield* verifyPending(key, record);
718
- const envelope = record.pending.envelope;
689
+ return yield* corrupt("process");
690
+ }
691
+ });
719
692
 
720
- const outcome = yield* admissionSemaphore.withPermit(
721
- admitPreparedInput(admission.submit(envelope), limits.admissionTimeoutMillis).pipe(
722
- Effect.tap((outcome) =>
723
- outcome._tag === "Receipt" ? failpoint.hit("schedule:admission:after") : Effect.void,
724
- ),
693
+ const deliverPending = Effect.fnUntraced(function* (
694
+ key: ScheduleKey,
695
+ record: ScheduleRecord,
696
+ nowMillis: number,
697
+ ) {
698
+ if (
699
+ record.pending === null ||
700
+ record.pending.retry.parked === true ||
701
+ record.pending.retry.nextAttemptAtMillis > nowMillis
702
+ ) {
703
+ return record;
704
+ }
705
+ yield* verifyPending(key, record);
706
+ const envelope = record.pending.envelope;
707
+
708
+ const outcome = yield* admissionSemaphore.withPermit(
709
+ admitPreparedInput(admission.submit(envelope), limits.admissionTimeoutMillis).pipe(
710
+ Effect.tap((outcome) =>
711
+ outcome._tag === "Receipt" ? failpoint.hit("schedule:admission:after") : Effect.void,
725
712
  ),
726
- );
727
-
728
- const completedAtMillis = yield* currentMillis;
713
+ ),
714
+ );
729
715
 
730
- if (outcome._tag === "Receipt") {
731
- if (outcome.receipt.threadId !== envelope.threadId) {
732
- return yield* corrupt("admission");
733
- }
716
+ const completedAtMillis = yield* currentMillis;
734
717
 
735
- return yield* store
736
- .change(key, {
737
- _tag: "Complete",
738
- occurrenceId: envelope.occurrenceId,
739
- receipt: outcome.receipt,
740
- nowMillis: completedAtMillis,
741
- })
742
- .pipe(
743
- Effect.catchTag("ScheduleStorageError", (error) =>
744
- error.reason === "unavailable"
745
- ? retry(key, record, "storage", completedAtMillis)
746
- : Effect.fail(error),
747
- ),
748
- );
718
+ if (outcome._tag === "Receipt") {
719
+ if (outcome.receipt.threadId !== envelope.threadId) {
720
+ return yield* corrupt("admission");
749
721
  }
750
- if (outcome._tag === "Refused") {
751
- const refusal: ScheduleRefusal = {
752
- atMillis: completedAtMillis,
753
- intendedAtMillis: envelope.intendedAtMillis,
722
+
723
+ return yield* store
724
+ .change(key, {
725
+ _tag: "Complete",
754
726
  occurrenceId: envelope.occurrenceId,
755
- phase: "admission",
756
- code: outcome.error.code,
757
- };
758
-
759
- return yield* store
760
- .change(key, {
761
- _tag: "Refuse",
762
- occurrenceId: envelope.occurrenceId,
763
- refusal,
764
- nowMillis: completedAtMillis,
765
- })
766
- .pipe(
767
- Effect.catchTag("ScheduleStorageError", (error) =>
768
- error.reason === "unavailable"
769
- ? retry(key, record, "storage", completedAtMillis)
770
- : Effect.fail(error),
771
- ),
772
- );
773
- }
727
+ receipt: outcome.receipt,
728
+ nowMillis: completedAtMillis,
729
+ })
730
+ .pipe(
731
+ Effect.catchTag("ScheduleStorageError", (error) =>
732
+ error.reason === "unavailable"
733
+ ? retry(key, record, "storage", completedAtMillis)
734
+ : Effect.fail(error),
735
+ ),
736
+ );
737
+ }
738
+ if (outcome._tag === "Refused") {
739
+ const refusal: ScheduleRefusal = {
740
+ atMillis: completedAtMillis,
741
+ intendedAtMillis: envelope.intendedAtMillis,
742
+ occurrenceId: envelope.occurrenceId,
743
+ phase: "admission",
744
+ code: outcome.error.code,
745
+ };
774
746
 
775
- return yield* retry(key, record, outcome.reason, completedAtMillis);
776
- });
747
+ return yield* store
748
+ .change(key, {
749
+ _tag: "Refuse",
750
+ occurrenceId: envelope.occurrenceId,
751
+ refusal,
752
+ nowMillis: completedAtMillis,
753
+ })
754
+ .pipe(
755
+ Effect.catchTag("ScheduleStorageError", (error) =>
756
+ error.reason === "unavailable"
757
+ ? retry(key, record, "storage", completedAtMillis)
758
+ : Effect.fail(error),
759
+ ),
760
+ );
761
+ }
777
762
 
778
- const processRecord = Effect.fnUntraced(function* (
779
- key: ScheduleKey,
780
- initial: ScheduleRecord,
781
- nowMillis: number,
782
- ): Effect.fn.Return<ScheduleRecord, ScheduleProcessFailure> {
783
- if (initial.pending !== null) return yield* deliverPending(key, initial, nowMillis);
784
- if (
785
- initial.state !== "active" ||
786
- initial.nextAtMillis === null ||
787
- initial.nextAtMillis > nowMillis
788
- ) {
789
- return initial;
790
- }
763
+ return yield* retry(key, record, outcome.reason, completedAtMillis);
764
+ });
791
765
 
792
- const currentInputDigest = yield* withCrypto(digestJson(initial.configuration.input)).pipe(
793
- Effect.mapError(() =>
794
- ScheduleStorageError.make({ operation: "prepare", reason: "unavailable" }),
795
- ),
796
- );
766
+ const processRecord = Effect.fnUntraced(function* (
767
+ key: ScheduleKey,
768
+ initial: ScheduleRecord,
769
+ nowMillis: number,
770
+ ): Effect.fn.Return<ScheduleRecord, ScheduleProcessFailure> {
771
+ if (initial.pending !== null) return yield* deliverPending(key, initial, nowMillis);
772
+ if (
773
+ initial.state !== "active" ||
774
+ initial.nextAtMillis === null ||
775
+ initial.nextAtMillis > nowMillis
776
+ ) {
777
+ return initial;
778
+ }
797
779
 
798
- if (currentInputDigest !== initial.configuration.inputDigest) {
799
- return yield* corrupt("prepare");
800
- }
780
+ const currentInputDigest = yield* withCrypto(digestJson(initial.configuration.input)).pipe(
781
+ Effect.mapError(() =>
782
+ ScheduleStorageError.make({ operation: "prepare", reason: "unavailable" }),
783
+ ),
784
+ );
801
785
 
802
- const due = yield* Effect.fromResult(
803
- scheduleDueOccurrence(initial.configuration.timing, initial.nextAtMillis, nowMillis),
804
- );
786
+ if (currentInputDigest !== initial.configuration.inputDigest) {
787
+ return yield* corrupt("prepare");
788
+ }
805
789
 
806
- if (due === null) return initial;
790
+ const due = yield* Effect.fromResult(
791
+ scheduleDueOccurrence(initial.configuration.timing, initial.nextAtMillis, nowMillis),
792
+ );
807
793
 
808
- const occurrenceId = yield* withCrypto(
809
- digestJson({
810
- schemaVersion: 1,
811
- owner: initial.owner,
812
- scheduleId: initial.scheduleId,
813
- configurationRevision: initial.configurationRevision,
814
- intendedAtMillis: due.intendedAtMillis,
815
- }),
816
- ).pipe(Effect.mapError(() => corrupt("prepare")));
794
+ if (due === null) return initial;
817
795
 
818
- const occurrence = {
819
- key,
796
+ const occurrenceId = yield* withCrypto(
797
+ digestJson({
798
+ schemaVersion: 1,
799
+ owner: initial.owner,
800
+ scheduleId: initial.scheduleId,
820
801
  configurationRevision: initial.configurationRevision,
821
- configuration: initial.configuration,
822
802
  intendedAtMillis: due.intendedAtMillis,
823
- occurrenceId,
824
- };
803
+ }),
804
+ ).pipe(Effect.mapError(() => corrupt("prepare")));
825
805
 
826
- const authorization = yield* authorizer.prepare(occurrence).pipe(Effect.result);
827
-
828
- if (Result.isFailure(authorization)) {
829
- if (authorization.failure._tag === "ScheduleStorageError") {
830
- return yield* authorization.failure;
831
- }
832
-
833
- return yield* store.change(key, {
834
- _tag: "DenyPreparation",
835
- expectedRevision: initial.configurationRevision,
836
- expectedCursor: initial.nextAtMillis,
837
- refusal: {
838
- atMillis: nowMillis,
839
- intendedAtMillis: due.intendedAtMillis,
840
- occurrenceId,
841
- phase: "preparation",
842
- code: authorization.failure.code,
843
- },
844
- nowMillis,
845
- });
846
- }
806
+ const occurrence = {
807
+ key,
808
+ configurationRevision: initial.configurationRevision,
809
+ configuration: initial.configuration,
810
+ intendedAtMillis: due.intendedAtMillis,
811
+ occurrenceId,
812
+ };
847
813
 
848
- const threadId =
849
- initial.configuration.destination._tag === "ExistingThread"
850
- ? initial.configuration.destination.threadId
851
- : Schema.decodeSync(ThreadId)(`schedule-thread:${occurrenceId}`);
814
+ const authorization = yield* authorizer.prepare(occurrence).pipe(Effect.result);
852
815
 
853
- const admissionKey = Schema.decodeSync(IdempotencyKey)(`schedule-occurrence:${occurrenceId}`);
816
+ if (Result.isFailure(authorization)) {
817
+ if (authorization.failure._tag === "ScheduleStorageError") {
818
+ return yield* authorization.failure;
819
+ }
854
820
 
855
- const prepared = yield* store.change(key, {
856
- _tag: "Prepare",
821
+ return yield* store.change(key, {
822
+ _tag: "DenyPreparation",
857
823
  expectedRevision: initial.configurationRevision,
858
824
  expectedCursor: initial.nextAtMillis,
859
- envelope: {
860
- schemaVersion: 1,
861
- owner: initial.owner,
862
- scheduleId: initial.scheduleId,
863
- configurationRevision: initial.configurationRevision,
825
+ refusal: {
826
+ atMillis: nowMillis,
864
827
  intendedAtMillis: due.intendedAtMillis,
865
- preparedAtMillis: nowMillis,
866
828
  occurrenceId,
867
- threadId,
868
- deliveryPrincipal: initial.configuration.deliveryPrincipal,
869
- ...(initial.configuration.admissionGroup === undefined
870
- ? {}
871
- : { admissionGroup: initial.configuration.admissionGroup }),
872
- ...(initial.configuration.admissionFence === undefined
873
- ? {}
874
- : { admissionFence: initial.configuration.admissionFence }),
875
- agentId: initial.configuration.agentId,
876
- definitions: initial.configuration.definitions,
877
- input: initial.configuration.input,
878
- inputDigest: initial.configuration.inputDigest,
879
- admissionKey,
880
- authorization: authorization.success,
829
+ phase: "preparation",
830
+ code: authorization.failure.code,
881
831
  },
882
- nextAtMillis: due.nextAtMillis,
883
- skippedRange: due.skippedRange,
884
832
  nowMillis,
885
833
  });
834
+ }
835
+
836
+ const threadId =
837
+ initial.configuration.destination._tag === "ExistingThread"
838
+ ? initial.configuration.destination.threadId
839
+ : Schema.decodeSync(ThreadId)(`schedule-thread:${occurrenceId}`);
886
840
 
887
- return yield* deliverPending(key, prepared, nowMillis);
841
+ const admissionKey = Schema.decodeSync(IdempotencyKey)(`schedule-occurrence:${occurrenceId}`);
842
+
843
+ const prepared = yield* store.change(key, {
844
+ _tag: "Prepare",
845
+ expectedRevision: initial.configurationRevision,
846
+ expectedCursor: initial.nextAtMillis,
847
+ envelope: {
848
+ schemaVersion: 1,
849
+ owner: initial.owner,
850
+ scheduleId: initial.scheduleId,
851
+ configurationRevision: initial.configurationRevision,
852
+ intendedAtMillis: due.intendedAtMillis,
853
+ preparedAtMillis: nowMillis,
854
+ occurrenceId,
855
+ threadId,
856
+ deliveryPrincipal: initial.configuration.deliveryPrincipal,
857
+ ...(initial.configuration.admissionGroup === undefined
858
+ ? {}
859
+ : { admissionGroup: initial.configuration.admissionGroup }),
860
+ ...(initial.configuration.admissionFence === undefined
861
+ ? {}
862
+ : { admissionFence: initial.configuration.admissionFence }),
863
+ agentId: initial.configuration.agentId,
864
+ definitions: initial.configuration.definitions,
865
+ input: initial.configuration.input,
866
+ inputDigest: initial.configuration.inputDigest,
867
+ admissionKey,
868
+ authorization: authorization.success,
869
+ },
870
+ nextAtMillis: due.nextAtMillis,
871
+ skippedRange: due.skippedRange,
872
+ nowMillis,
888
873
  });
889
874
 
890
- const process = Effect.fnUntraced(function* (key: ScheduleKey) {
891
- const nowMillis = yield* currentMillis;
892
- const record = yield* store.get(key);
875
+ return yield* deliverPending(key, prepared, nowMillis);
876
+ });
877
+
878
+ const process = Effect.fnUntraced(function* (key: ScheduleKey) {
879
+ const nowMillis = yield* currentMillis;
880
+ const record = yield* store.get(key);
893
881
 
894
- if (record === null) return yield* notFound(key);
882
+ if (record === null) return yield* notFound(key);
895
883
 
896
- const processed = yield* processRecord(key, record, nowMillis).pipe(
897
- Effect.catchTag("ScheduleConflict", () =>
898
- store
899
- .get(key)
900
- .pipe(
901
- Effect.flatMap((current) =>
902
- current === null
903
- ? Effect.fail(notFound(key))
904
- : processRecord(key, current, nowMillis),
905
- ),
884
+ const processed = yield* processRecord(key, record, nowMillis).pipe(
885
+ Effect.catchTag("ScheduleConflict", () =>
886
+ store
887
+ .get(key)
888
+ .pipe(
889
+ Effect.flatMap((current) =>
890
+ current === null
891
+ ? Effect.fail(notFound(key))
892
+ : processRecord(key, current, nowMillis),
906
893
  ),
907
- ),
908
- );
894
+ ),
895
+ ),
896
+ );
909
897
 
910
- return processed;
911
- });
898
+ return processed;
899
+ });
912
900
 
913
- const runDue = Effect.fnUntraced(function* (owner?: ScheduleOwner) {
914
- const nowMillis = yield* currentMillis;
915
- let after: ScheduleDueCursor | undefined;
916
- let processed = 0;
917
- let failed = 0;
918
-
919
- while (true) {
920
- const due = yield* store.due(nowMillis, limits.dueBatchSize, owner, after);
921
-
922
- if (due.length === 0) break;
923
- yield* Effect.forEach(
924
- due,
925
- (key) =>
926
- process(key).pipe(
927
- Effect.matchCauseEffect({
928
- onSuccess: () =>
929
- Effect.sync(() => {
930
- processed += 1;
931
- }),
932
- onFailure: (cause) =>
933
- Cause.hasInterrupts(cause)
934
- ? Effect.interrupt
935
- : Effect.gen(function* () {
936
- failed += 1;
937
- yield* Effect.logWarning("Schedule processing failed").pipe(
938
- Effect.annotateLogs({
939
- failureTag: Option.match(Cause.findErrorOption(cause), {
940
- onNone: () => "Defect",
941
- onSome: (error) => error._tag,
942
- }),
901
+ const runDue = Effect.fnUntraced(function* (owner?: ScheduleOwner) {
902
+ const nowMillis = yield* currentMillis;
903
+ let after: ScheduleDueCursor | undefined;
904
+ let processed = 0;
905
+ let failed = 0;
906
+
907
+ while (true) {
908
+ const due = yield* store.due(nowMillis, limits.dueBatchSize, owner, after);
909
+
910
+ if (due.length === 0) break;
911
+ yield* Effect.forEach(
912
+ due,
913
+ (key) =>
914
+ process(key).pipe(
915
+ Effect.matchCauseEffect({
916
+ onSuccess: () =>
917
+ Effect.sync(() => {
918
+ processed += 1;
919
+ }),
920
+ onFailure: (cause) =>
921
+ Cause.hasInterrupts(cause)
922
+ ? Effect.interrupt
923
+ : Effect.gen(function* () {
924
+ failed += 1;
925
+ yield* Effect.logWarning("Schedule processing failed").pipe(
926
+ Effect.annotateLogs({
927
+ failureTag: Option.match(Cause.findErrorOption(cause), {
928
+ onNone: () => "Defect",
929
+ onSome: (error) => error._tag,
943
930
  }),
944
- );
945
- }),
946
- }),
947
- ),
948
- { concurrency: limits.admissionConcurrency },
949
- );
950
- after = due.at(-1);
951
- if (due.length < limits.dueBatchSize) break;
952
- }
953
-
954
- return { processed, failed };
955
- });
931
+ }),
932
+ );
933
+ }),
934
+ }),
935
+ ),
936
+ { concurrency: limits.admissionConcurrency },
937
+ );
938
+ after = due.at(-1);
939
+ if (due.length < limits.dueBatchSize) break;
940
+ }
956
941
 
957
- return ScheduleDriver.of({ process, runDue });
942
+ return { processed, failed };
958
943
  });
959
944
 
945
+ return ScheduleDriver.of({ process, runDue });
946
+ });
947
+
960
948
  const corrupt = (operation: string): ScheduleStorageError =>
961
949
  ScheduleStorageError.make({ operation, reason: "corrupt" });
962
950