@effect-agent/platform-cloudflare 0.1.0-beta.9 → 0.1.0-beta.91

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 (100) hide show
  1. package/dist/Alarm.d.mts +188 -0
  2. package/dist/Alarm.mjs +451 -0
  3. package/dist/Alarm.mjs.map +1 -0
  4. package/dist/BrowserRestCapture.d.mts +35 -0
  5. package/dist/BrowserRestCapture.mjs +238 -0
  6. package/dist/BrowserRestCapture.mjs.map +1 -0
  7. package/dist/BrowserRestCrawl.d.mts +17 -0
  8. package/dist/BrowserRestCrawl.mjs +244 -0
  9. package/dist/BrowserRestCrawl.mjs.map +1 -0
  10. package/dist/CloudflareAiGateway.d.mts +64 -0
  11. package/dist/CloudflareAiGateway.mjs +70 -0
  12. package/dist/CloudflareAiGateway.mjs.map +1 -0
  13. package/dist/CloudflareBindings.d.mts +115 -0
  14. package/dist/CloudflareBindings.mjs +113 -0
  15. package/dist/CloudflareBindings.mjs.map +1 -0
  16. package/dist/CloudflareBrowser-DV6kya1H.mjs +495 -0
  17. package/dist/CloudflareBrowser-DV6kya1H.mjs.map +1 -0
  18. package/dist/CloudflareBrowser-Doe1M7R5.d.mts +80 -0
  19. package/dist/CloudflareBrowser.d.mts +2 -0
  20. package/dist/CloudflareBrowser.mjs +2 -0
  21. package/dist/CloudflareCodeMode.d.mts +44 -0
  22. package/dist/CloudflareCodeMode.mjs +616 -0
  23. package/dist/CloudflareCodeMode.mjs.map +1 -0
  24. package/dist/CloudflareConfig-f3CqTel1.d.mts +101 -0
  25. package/dist/CloudflareConfig.d.mts +2 -0
  26. package/dist/CloudflareConfig.mjs +122 -0
  27. package/dist/CloudflareConfig.mjs.map +1 -0
  28. package/dist/CloudflareMemory.d.mts +142 -0
  29. package/dist/CloudflareMemory.mjs +202 -0
  30. package/dist/CloudflareMemory.mjs.map +1 -0
  31. package/dist/CloudflareScheduling.d.mts +52 -0
  32. package/dist/CloudflareScheduling.mjs +396 -0
  33. package/dist/CloudflareScheduling.mjs.map +1 -0
  34. package/dist/CloudflareSubscriptions.d.mts +87 -0
  35. package/dist/CloudflareSubscriptions.mjs +530 -0
  36. package/dist/CloudflareSubscriptions.mjs.map +1 -0
  37. package/dist/CloudflareThreadClient.d.mts +1496 -0
  38. package/dist/CloudflareThreadClient.mjs +382 -0
  39. package/dist/CloudflareThreadClient.mjs.map +1 -0
  40. package/dist/InteractiveBrowser-DUE_m12-.d.mts +161 -0
  41. package/dist/InteractiveBrowser-DupDtfw2.mjs +1169 -0
  42. package/dist/InteractiveBrowser-DupDtfw2.mjs.map +1 -0
  43. package/dist/InteractiveBrowser.d.mts +2 -0
  44. package/dist/InteractiveBrowser.mjs +2 -0
  45. package/dist/ProtectedBrowser.d.mts +123 -0
  46. package/dist/ProtectedBrowser.mjs +1135 -0
  47. package/dist/ProtectedBrowser.mjs.map +1 -0
  48. package/dist/ThreadObject-CIbmXZCY.d.mts +812 -0
  49. package/dist/ThreadObject-DvRG0dDz.mjs +835 -0
  50. package/dist/ThreadObject-DvRG0dDz.mjs.map +1 -0
  51. package/dist/ThreadObject.d.mts +2 -0
  52. package/dist/ThreadObject.mjs +3 -0
  53. package/dist/WakeScheduler.d.mts +23 -0
  54. package/dist/WakeScheduler.mjs +57 -0
  55. package/dist/WakeScheduler.mjs.map +1 -0
  56. package/dist/boundary-BguazVkh.mjs +42 -0
  57. package/dist/boundary-BguazVkh.mjs.map +1 -0
  58. package/dist/index.d.mts +13 -1548
  59. package/dist/index.mjs +13 -1636
  60. package/dist/prepared-admission-DU0_T-ev.mjs +73 -0
  61. package/dist/prepared-admission-DU0_T-ev.mjs.map +1 -0
  62. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  63. package/package.json +1 -53
  64. package/src/Alarm.ts +973 -0
  65. package/src/BrowserRestCapture.ts +477 -0
  66. package/src/BrowserRestCrawl.ts +540 -0
  67. package/src/CloudflareAiGateway.ts +170 -0
  68. package/src/CloudflareBindings.ts +199 -0
  69. package/src/CloudflareBrowser.ts +15 -0
  70. package/src/{code-mode-executor.ts → CloudflareCodeMode.ts} +372 -204
  71. package/src/{config.ts → CloudflareConfig.ts} +9 -8
  72. package/src/CloudflareMemory.ts +420 -0
  73. package/src/CloudflareScheduling.ts +747 -0
  74. package/src/CloudflareSubscriptions.ts +1041 -0
  75. package/src/CloudflareThreadClient.ts +809 -0
  76. package/src/InteractiveBrowser.ts +2354 -0
  77. package/src/ProtectedBrowser.ts +9 -0
  78. package/src/ThreadObject.ts +1179 -0
  79. package/src/{wake-scheduler.ts → WakeScheduler.ts} +32 -26
  80. package/src/index.ts +12 -23
  81. package/src/internal/boundary.ts +55 -0
  82. package/src/internal/browser-quick-action.ts +857 -0
  83. package/src/internal/browser-session-lifecycle.ts +160 -0
  84. package/src/internal/layers.ts +751 -0
  85. package/src/internal/message-delivery.ts +152 -0
  86. package/src/internal/prepared-admission.ts +116 -0
  87. package/src/internal/progress-wait.ts +121 -0
  88. package/src/internal/transport.ts +44 -0
  89. package/src/protected-browser/binding.ts +313 -0
  90. package/src/protected-browser/host.ts +344 -0
  91. package/src/protected-browser/inspect-frame.ts +125 -0
  92. package/src/protected-browser/native.ts +445 -0
  93. package/src/protected-browser/policy.ts +821 -0
  94. package/dist/index.mjs.map +0 -1
  95. package/src/alarm.ts +0 -334
  96. package/src/bindings.ts +0 -145
  97. package/src/client.ts +0 -646
  98. package/src/conversation-object.ts +0 -768
  99. package/src/layers.ts +0 -376
  100. package/src/transport.ts +0 -38
@@ -0,0 +1,188 @@
1
+ import { DurableObjectContext } from "./CloudflareBindings.mjs";
2
+ import { o as CloudflareDurableRuntimeConfig } from "./CloudflareConfig-f3CqTel1.mjs";
3
+ import { Cause, Context, Deferred, Effect, Layer, Option, Schema } from "effect";
4
+ import { DurableBindingFailure } from "effect-agent/agent-registration";
5
+ import { DurableAgentRuntime, DurableWorkerFailure } from "effect-agent/durable-agent-runtime";
6
+ import { SubmissionLedger } from "effect-agent/submission-ledger";
7
+ import { ThreadProjectionError, ThreadProjectionMaintenance } from "effect-agent/thread-projection-maintenance";
8
+ import { SqlClient } from "effect/unstable/sql/SqlClient";
9
+ declare namespace Alarm_d_exports {
10
+ export { DurableAlarmError, DurableAlarmService, MaintenancePassFailure, MaintenancePassReport, ThreadHostMaintenance, ThreadMaintenance, ThreadMaintenanceFailpoint, ThreadMaintenanceFailpointHandler, ThreadMaintenanceFailpointLocation, ThreadMessageDelivery, ThreadMutationGate, ThreadPublication, ThreadPublicationService, publishCommitted };
11
+ }
12
+ declare const DurableAlarmError_base: Schema.Class<DurableAlarmError, Schema.TaggedStruct<"DurableAlarmError", {
13
+ readonly operation: Schema.String;
14
+ readonly message: Schema.String;
15
+ readonly cause: Schema.optionalKey<Schema.Defect>;
16
+ }>, Cause.YieldableError>;
17
+ /**
18
+ * The single multiplexed Durable Object alarm (decision D-P6-2). A Durable Object has ONE
19
+ * alarm slot; every cadence the Node host ran on fibers (wake scan, lease expiry, settlement
20
+ * and abort re-checks, retry backoff) multiplexes into one idempotent maintenance pass, and
21
+ * the slot always holds the EARLIEST deadline any caller asked for.
22
+ *
23
+ * The alarm invariant (plan §1.4): every committed actionable mutation carries a newer durable
24
+ * maintenance generation and a committed alarm. Stable externally-driven waits may be
25
+ * nonterminal without retaining an alarm; their resolving mutation advances the generation and
26
+ * restores the alarm atomically.
27
+ */
28
+ /** The Durable Object alarm API failed; surfaces on host entry points as a typed refusal. */
29
+ declare class DurableAlarmError extends DurableAlarmError_base {}
30
+ declare const DurableAlarmService_base: Context.ServiceClass<DurableAlarmService, "@effect-agent/platform-cloudflare/DurableAlarmService", {
31
+ /** The scheduled deadline in epoch milliseconds, if any. */
32
+ readonly scheduled: Effect.Effect<Option.Option<number>, DurableAlarmError>;
33
+ /** Replace the slot with this deadline. */
34
+ readonly scheduleAt: (epochMillis: number) => Effect.Effect<void, DurableAlarmError>;
35
+ /** Keep the EARLIER of the existing deadline and this one (the multiplexing rule). */
36
+ readonly ensureScheduledBy: (epochMillis: number) => Effect.Effect<void, DurableAlarmError>;
37
+ /**
38
+ * Arm an immediate alarm (the durable, coalescing local wake) — DEFERRED while a
39
+ * maintenance pass is executing. Workerd cancels an in-flight alarm handler when a new
40
+ * EARLIER deadline is written during its execution (`requestScheduledAlarm`), and the
41
+ * maintenance pass runs INSIDE the alarm handler: an immediate wake landing mid-pass
42
+ * (a routed port mutation, a sibling's `wake()`, the coordinator's own local notify)
43
+ * would kill the running Attempt — manufacturing an ownership loss no real eviction
44
+ * caused, and routing open uncertain-class Tool Calls into spurious Unknown Outcomes.
45
+ * Deferral is contract-safe: wakes are droppable hints, every mutating entry point
46
+ * pre-arms BEFORE its first durable mutation (the alarm invariant never rests on this
47
+ * call). The pass's durable generation check observes any racing mutation, so the
48
+ * in-memory hint does not need to be flushed after a stable wait is acknowledged.
49
+ */
50
+ readonly scheduleNow: Effect.Effect<void, DurableAlarmError>;
51
+ /**
52
+ * Run one maintenance pass with wake deferral (see `scheduleNow`). Calls made while `body`
53
+ * executes are droppable promptness hints; correctness rests on the durable generation.
54
+ */
55
+ readonly withWakesDeferred: <A, E, R>(body: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
56
+ /** Clear the slot; correctness-sensitive clears live in maintenance generation transactions. */
57
+ readonly cancel: Effect.Effect<void, DurableAlarmError>;
58
+ }>;
59
+ /** `ctx.storage` alarm slot as an Effect service; storage is truth, never a memory field. */
60
+ declare class DurableAlarmService extends DurableAlarmService_base {
61
+ static readonly layer: Layer.Layer<DurableAlarmService, never, DurableObjectContext | SqlClient>;
62
+ }
63
+ declare const MaintenancePassReport_base: Schema.Class<MaintenancePassReport, Schema.Struct<{
64
+ /** `caught-up` ran no runtime work (publication may be pending); `actionable` ran recovery. */
65
+ readonly phase: Schema.Literals<readonly ["caught-up", "actionable"]>;
66
+ /** Recovery decisions executed (or deferred) BEFORE any new claim in this pass. */
67
+ readonly recovered: Schema.Int;
68
+ /** Whether the head Attempt settled. Joined input may settle with that head. */
69
+ readonly settled: Schema.Int;
70
+ /** Submissions still nonterminal after the pass (suspended/unknown lanes stay honest). */
71
+ readonly nonterminal: Schema.Int;
72
+ /** `rearmed` for dirty/autonomous work, `cleared` for stable waits or settlement. */
73
+ readonly alarm: Schema.Literals<readonly ["rearmed", "cleared"]>;
74
+ }>, {}>;
75
+ /** What one maintenance pass did — auditable evidence mirroring `NodeDurableHost`'s report. */
76
+ declare class MaintenancePassReport extends MaintenancePassReport_base {}
77
+ /** Fault boundaries around every maintenance-owned durable mutation. */
78
+ type ThreadMaintenanceFailpointLocation = "maintenance:dirty:before" | "maintenance:dirty:after" | "maintenance:mutation:armed" | "maintenance:mutation:finished" | "maintenance:ensure:before" | "maintenance:ensure:after" | "maintenance:begin:before" | "maintenance:begin:after" | "maintenance:select:before" | "maintenance:select:after" | "maintenance:finish:before" | "maintenance:finish:after";
79
+ type ThreadMaintenanceFailpointHandler = (location: ThreadMaintenanceFailpointLocation) => Effect.Effect<void>;
80
+ declare const ThreadMaintenanceFailpoint_base: Context.ServiceClass<ThreadMaintenanceFailpoint, "@effect-agent/platform-cloudflare/ThreadMaintenanceFailpoint", {
81
+ readonly hit: ThreadMaintenanceFailpointHandler;
82
+ }>;
83
+ /** Test-only fault authority; production uses the inert layer. */
84
+ declare class ThreadMaintenanceFailpoint extends ThreadMaintenanceFailpoint_base {
85
+ static readonly layer: Layer.Layer<ThreadMaintenanceFailpoint, never, never>;
86
+ }
87
+ /**
88
+ * Durable host publication of canonical records and ledger approval/abort/resolution intents.
89
+ * The host owns schema-versioned cursors, destination idempotency and acknowledgement. Delivery
90
+ * is at least once. Hooks must not write the alarm slot or mutate the supplied raw source ports.
91
+ *
92
+ * `invalidate`, `prepareGeneration` and `pendingDeadline` must be bounded local operations.
93
+ * `prepareGeneration` durably invalidates a scan only when its generation changes; repeated
94
+ * calls must preserve partial scan progress. It runs with no source mutation in flight.
95
+ * `drain` performs bounded delivery and persists retries before returning. A pending deadline
96
+ * defers runtime recovery/Attempts, allowing committed host publications to drain first.
97
+ * Unexpected hook failures leave the prearmed generation for retry. Hooks acquire per-call
98
+ * resources with Effect.scoped; Layer construction owns incarnation resources (eviction need
99
+ * not run finalizers). Do not hold a local hook behind network I/O or call back into producers.
100
+ */
101
+ interface ThreadPublicationService {
102
+ readonly invalidate: Effect.Effect<void, DurableAlarmError>;
103
+ readonly prepareGeneration: (generation: bigint) => Effect.Effect<void, DurableAlarmError>;
104
+ readonly drain: Effect.Effect<void, DurableAlarmError>;
105
+ readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;
106
+ }
107
+ declare const ThreadPublication_base: Context.ServiceClass<ThreadPublication, "@effect-agent/platform-cloudflare/ThreadPublication", ThreadPublicationService>;
108
+ /** Opt in with `ThreadObject.layer(registrations, { publication: Layer.effect(ThreadPublication)(...) })`. */
109
+ declare class ThreadPublication extends ThreadPublication_base {
110
+ static readonly layer: Layer.Layer<ThreadPublication, never, never>;
111
+ }
112
+ /**
113
+ * Host-assembled message recovery, supplied by ThreadObject.layer even when the application
114
+ * rebuilds ThreadMaintenance. These obligations outlive source Runs and never defer a ready
115
+ * source Attempt while a destination is processing an accepted message.
116
+ */
117
+ declare const ThreadMessageDelivery: Context.Reference<{
118
+ readonly drain: Effect.Effect<void, DurableAlarmError>;
119
+ /** Drain inserts and due retries during source work, finishing the bounded wave on completion. */
120
+ readonly drainUntil?: (finished: Deferred.Deferred<void>) => Effect.Effect<void, DurableAlarmError>;
121
+ readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;
122
+ }>;
123
+ /**
124
+ * Application obligations sharing this Object's alarm. The deadline read is local and
125
+ * read-only. Drain beside the native Attempt and always finish one initial bounded wave,
126
+ * even when `finished` was already signalled. Then stop starting new waves on that signal
127
+ * and finish the bounded current wave before returning. Native maintenance joins that work
128
+ * before acknowledging a generation. Mutations use the same ThreadMutationGate; hooks never
129
+ * write the raw alarm slot. Pending host work does not defer a ready model Attempt.
130
+ */
131
+ declare const ThreadHostMaintenance: Context.Reference<{
132
+ readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;
133
+ readonly drainUntil: (finished: Deferred.Deferred<void>) => Effect.Effect<void, DurableAlarmError>;
134
+ }>;
135
+ /** @internal A committed source operation must not become a failed operation because delivery failed. */
136
+ declare const publishCommitted: Effect.Effect<void, never, ThreadPublication>;
137
+ declare const ThreadMutationGate_base: Context.ServiceClass<ThreadMutationGate, "@effect-agent/platform-cloudflare/internal/ThreadMutationGate", {
138
+ readonly withMutation: <A, E, R>(body: Effect.Effect<A, E, R>,
139
+ /** Indexed host work has its own durable deadline and does not invalidate ledger recovery. */
140
+ options?: {
141
+ readonly invalidatesRecovery: boolean;
142
+ }) => Effect.Effect<A, E | DurableAlarmError, R>;
143
+ readonly withSnapshot: <A, E, R>(body: (active: number) => Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
144
+ }>;
145
+ /**
146
+ * Shared prearm/acknowledgement boundary for ingress and runtime-owned producers.
147
+ * `ThreadObject.layer` provides this same instance in its Services. Rebuilt runtime/maintenance
148
+ * Layers must reuse that instance; a second gate cannot observe the native producers' activity.
149
+ */
150
+ declare class ThreadMutationGate extends ThreadMutationGate_base {
151
+ static readonly layer: Layer.Layer<ThreadMutationGate, never, CloudflareDurableRuntimeConfig | DurableObjectContext | SqlClient | ThreadMaintenanceFailpoint>;
152
+ }
153
+ type MaintenancePassFailure = DurableWorkerFailure | DurableBindingFailure | DurableAlarmError | ThreadProjectionError;
154
+ declare const ThreadMaintenance_base: Context.ServiceClass<ThreadMaintenance, "@effect-agent/platform-cloudflare/ThreadMaintenance", {
155
+ /** One idempotent maintenance pass; failures propagate so workerd retries the alarm. */
156
+ readonly pass: Effect.Effect<MaintenancePassReport, MaintenancePassFailure>;
157
+ /**
158
+ * Constructor gate: initialize/inspect only the O(1) maintenance record and ensure a dirty
159
+ * generation has an alarm. It never scans the ledger or canonical history.
160
+ */
161
+ readonly ensureAlarm: Effect.Effect<void, MaintenancePassFailure>;
162
+ /**
163
+ * Serialize the pre-arm boundary with pass acknowledgement, advance the durable dirty
164
+ * generation and arm the alarm in one transaction BEFORE running the caller's mutation.
165
+ * A pass cannot acknowledge while that mutation remains in flight.
166
+ */
167
+ readonly withMutation: <A, E, R>(body: Effect.Effect<A, E, R>) => Effect.Effect<A, E | DurableAlarmError, R>;
168
+ }>;
169
+ /**
170
+ * Incremental, quiescent maintenance over a durable dirty/processed generation (issue #93).
171
+ *
172
+ * `pass` = generation snapshot/pre-arm → recovery → one head Attempt → generation acknowledgement:
173
+ *
174
+ * 1. One storage transaction reads dirty/processed and re-arms before work. A caught-up forced
175
+ * alarm takes an O(1) path without recovery, ledger scans, or canonical-history reads.
176
+ * 2. Recovery strictly precedes a new claim. One head Attempt advances the lane and requests
177
+ * a safe yield after ten minutes. The whole event has a fourteen-minute cooperative timeout.
178
+ * 3. The final transaction acknowledges only the generation observed at pass start. A racing
179
+ * mutation therefore remains `dirty > processed` and retains its atomically-established alarm.
180
+ * 4. Stable external waits acknowledge and clear. Autonomous retry, indeterminate, and lease
181
+ * recovery states leave their generation dirty and retain bounded backoff rearming.
182
+ */
183
+ declare class ThreadMaintenance extends ThreadMaintenance_base {
184
+ static readonly layer: Layer.Layer<ThreadMaintenance, never, ThreadMutationGate | ThreadPublication | ThreadProjectionMaintenance | DurableAgentRuntime | SubmissionLedger | DurableAlarmService | ThreadMaintenanceFailpoint | CloudflareDurableRuntimeConfig | DurableObjectContext | SqlClient>;
185
+ }
186
+ //#endregion
187
+ export { DurableAlarmError, DurableAlarmService, MaintenancePassFailure, MaintenancePassReport, ThreadHostMaintenance, ThreadMaintenance, ThreadMaintenanceFailpoint, ThreadMaintenanceFailpointHandler, ThreadMaintenanceFailpointLocation, ThreadMessageDelivery, ThreadMutationGate, ThreadPublication, ThreadPublicationService, publishCommitted, Alarm_d_exports as t };
188
+ //# sourceMappingURL=Alarm.d.mts.map
package/dist/Alarm.mjs ADDED
@@ -0,0 +1,451 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { DurableObjectContext } from "./CloudflareBindings.mjs";
3
+ import { CloudflareDurableRuntimeConfig } from "./CloudflareConfig.mjs";
4
+ import { r as safeCauseMessage } from "./boundary-BguazVkh.mjs";
5
+ import { Cause, Clock, Context, DateTime, Deferred, Effect, Exit, Fiber, Layer, Option, Random, Ref, Schema, Semaphore, Stream } from "effect";
6
+ import "effect-agent/agent-registration";
7
+ import { DurableAgentRuntime } from "effect-agent/durable-agent-runtime";
8
+ import { ThreadId } from "effect-agent/identifiers";
9
+ import { SubmissionLedger } from "effect-agent/submission-ledger";
10
+ import { ThreadProjectionMaintenance, drainDue } from "effect-agent/thread-projection-maintenance";
11
+ import { SqlClient } from "effect/unstable/sql/SqlClient";
12
+ //#region src/Alarm.ts
13
+ var Alarm_exports = /* @__PURE__ */ __exportAll({
14
+ DurableAlarmError: () => DurableAlarmError,
15
+ DurableAlarmService: () => DurableAlarmService,
16
+ MaintenancePassReport: () => MaintenancePassReport,
17
+ ThreadHostMaintenance: () => ThreadHostMaintenance,
18
+ ThreadMaintenance: () => ThreadMaintenance,
19
+ ThreadMaintenanceFailpoint: () => ThreadMaintenanceFailpoint,
20
+ ThreadMessageDelivery: () => ThreadMessageDelivery,
21
+ ThreadMutationGate: () => ThreadMutationGate,
22
+ ThreadPublication: () => ThreadPublication,
23
+ publishCommitted: () => publishCommitted
24
+ });
25
+ /**
26
+ * The single multiplexed Durable Object alarm (decision D-P6-2). A Durable Object has ONE
27
+ * alarm slot; every cadence the Node host ran on fibers (wake scan, lease expiry, settlement
28
+ * and abort re-checks, retry backoff) multiplexes into one idempotent maintenance pass, and
29
+ * the slot always holds the EARLIEST deadline any caller asked for.
30
+ *
31
+ * The alarm invariant (plan §1.4): every committed actionable mutation carries a newer durable
32
+ * maintenance generation and a committed alarm. Stable externally-driven waits may be
33
+ * nonterminal without retaining an alarm; their resolving mutation advances the generation and
34
+ * restores the alarm atomically.
35
+ */
36
+ /** The Durable Object alarm API failed; surfaces on host entry points as a typed refusal. */
37
+ var DurableAlarmError = class extends Schema.TaggedError()("DurableAlarmError", {
38
+ operation: Schema.String,
39
+ message: Schema.String,
40
+ cause: Schema.optionalKey(Schema.Defect())
41
+ }) {};
42
+ const alarmFailure = (operation) => (cause) => DurableAlarmError.make({
43
+ operation,
44
+ message: safeCauseMessage(cause, "The Cloudflare alarm API failed without a diagnostic"),
45
+ cause
46
+ });
47
+ const makeStorageOperation = Effect.map(SqlClient, (sql) => (operation, execute) => Effect.flatMap(Effect.serviceOption(sql.transactionService), (current) => {
48
+ const body = Effect.uninterruptible(Effect.tryPromise({
49
+ try: execute,
50
+ catch: alarmFailure(operation)
51
+ }));
52
+ return current._tag === "Some" ? body : Effect.scoped(Effect.andThen(sql.reserve.pipe(Effect.mapError(alarmFailure(operation))), body));
53
+ }));
54
+ /** `ctx.storage` alarm slot as an Effect service; storage is truth, never a memory field. */
55
+ var DurableAlarmService = class DurableAlarmService extends Context.Service()("@effect-agent/platform-cloudflare/DurableAlarmService") {
56
+ static layer = Layer.effect(DurableAlarmService)(Effect.gen(function* () {
57
+ const { ctx } = yield* DurableObjectContext;
58
+ /**
59
+ * In-memory pass bookkeeping — a pure CACHE, never state: a fresh incarnation has no
60
+ * running pass, and a deferred wake lost to eviction was only ever a promptness hint
61
+ * on top of the already-committed pre-armed alarm.
62
+ */
63
+ const runningPasses = yield* Ref.make(0);
64
+ const storageOperation = yield* makeStorageOperation;
65
+ const scheduled = storageOperation("get alarm", () => ctx.storage.getAlarm()).pipe(Effect.map((deadline) => deadline === null ? Option.none() : Option.some(deadline)));
66
+ const scheduleAt = (epochMillis) => storageOperation("set alarm", () => ctx.storage.setAlarm(epochMillis));
67
+ const ensureScheduledBy = (epochMillis) => storageOperation("ensure alarm", () => ctx.storage.transaction(async (transaction) => {
68
+ const existing = await transaction.getAlarm();
69
+ if (existing === null || existing > epochMillis) await transaction.setAlarm(epochMillis);
70
+ }));
71
+ const armNow = Clock.currentTimeMillis.pipe(Effect.flatMap((now) => ensureScheduledBy(now)));
72
+ const scheduleNow = Ref.get(runningPasses).pipe(Effect.flatMap((passes) => passes > 0 ? Effect.void : armNow));
73
+ const withWakesDeferred = (body) => Ref.update(runningPasses, (passes) => passes + 1).pipe(Effect.andThen(body), Effect.ensuring(Ref.update(runningPasses, (passes) => passes - 1)));
74
+ const cancel = storageOperation("delete alarm", () => ctx.storage.deleteAlarm());
75
+ return DurableAlarmService.of({
76
+ scheduled,
77
+ scheduleAt,
78
+ ensureScheduledBy,
79
+ scheduleNow,
80
+ withWakesDeferred,
81
+ cancel
82
+ });
83
+ }));
84
+ };
85
+ /** What one maintenance pass did — auditable evidence mirroring `NodeDurableHost`'s report. */
86
+ var MaintenancePassReport = class extends Schema.Class("@effect-agent/platform-cloudflare/MaintenancePassReport")({
87
+ /** `caught-up` ran no runtime work (publication may be pending); `actionable` ran recovery. */
88
+ phase: Schema.Literals(["caught-up", "actionable"]),
89
+ /** Recovery decisions executed (or deferred) BEFORE any new claim in this pass. */
90
+ recovered: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
91
+ /** Whether the head Attempt settled. Joined input may settle with that head. */
92
+ settled: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
93
+ /** Submissions still nonterminal after the pass (suspended/unknown lanes stay honest). */
94
+ nonterminal: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
95
+ /** `rearmed` for dirty/autonomous work, `cleared` for stable waits or settlement. */
96
+ alarm: Schema.Literals(["rearmed", "cleared"])
97
+ }) {};
98
+ /** Test-only fault authority; production uses the inert layer. */
99
+ var ThreadMaintenanceFailpoint = class extends Context.Service()("@effect-agent/platform-cloudflare/ThreadMaintenanceFailpoint") {
100
+ static layer = Layer.succeed(this)({ hit: () => Effect.void });
101
+ };
102
+ /** Opt in with `ThreadObject.layer(registrations, { publication: Layer.effect(ThreadPublication)(...) })`. */
103
+ var ThreadPublication = class extends Context.Service()("@effect-agent/platform-cloudflare/ThreadPublication") {
104
+ static layer = Layer.succeed(this)({
105
+ invalidate: Effect.void,
106
+ prepareGeneration: () => Effect.void,
107
+ drain: Effect.void,
108
+ pendingDeadline: Effect.succeed(Option.none())
109
+ });
110
+ };
111
+ /**
112
+ * Host-assembled message recovery, supplied by ThreadObject.layer even when the application
113
+ * rebuilds ThreadMaintenance. These obligations outlive source Runs and never defer a ready
114
+ * source Attempt while a destination is processing an accepted message.
115
+ */
116
+ const ThreadMessageDelivery = Context.Reference("@effect-agent/platform-cloudflare/ThreadMessageDelivery", { defaultValue: () => ({
117
+ drain: Effect.void,
118
+ pendingDeadline: Effect.succeed(Option.none())
119
+ }) });
120
+ /**
121
+ * Application obligations sharing this Object's alarm. The deadline read is local and
122
+ * read-only. Drain beside the native Attempt and always finish one initial bounded wave,
123
+ * even when `finished` was already signalled. Then stop starting new waves on that signal
124
+ * and finish the bounded current wave before returning. Native maintenance joins that work
125
+ * before acknowledging a generation. Mutations use the same ThreadMutationGate; hooks never
126
+ * write the raw alarm slot. Pending host work does not defer a ready model Attempt.
127
+ */
128
+ const ThreadHostMaintenance = Context.Reference("@effect-agent/platform-cloudflare/ThreadHostMaintenance", { defaultValue: () => ({
129
+ pendingDeadline: Effect.succeed(Option.none()),
130
+ drainUntil: () => Effect.void
131
+ }) });
132
+ const earliestDeadline = (left, right) => Option.isSome(left) ? Option.isSome(right) ? Option.some(Math.min(left.value, right.value)) : left : right;
133
+ /** @internal A committed source operation must not become a failed operation because delivery failed. */
134
+ const publishCommitted = Effect.gen(function* () {
135
+ const publication = yield* ThreadPublication;
136
+ yield* publication.invalidate.pipe(Effect.andThen(publication.drain));
137
+ }).pipe(Effect.catchCause((cause) => Cause.hasInterrupts(cause) ? Effect.interrupt : Effect.logError("Thread publication deferred after source commit", cause)));
138
+ const MaintenanceGeneration = Schema.BigIntFromString.check(Schema.isGreaterThanOrEqualToBigInt(0n));
139
+ /** Versioned, platform-private maintenance state stored through Durable Object KV. */
140
+ var ThreadMaintenanceState = class extends Schema.Class("@effect-agent/platform-cloudflare/ThreadMaintenanceState")({
141
+ schemaVersion: Schema.Literal(1),
142
+ dirty: MaintenanceGeneration,
143
+ processed: MaintenanceGeneration,
144
+ nonterminal: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
145
+ /** One physical-owner cursor; old single-lane records need no conversion. */
146
+ lastServedThreadId: Schema.optionalKey(ThreadId)
147
+ }) {};
148
+ const MAINTENANCE_STATE_KEY = "effect-agent:thread-maintenance:v1";
149
+ const decodeMaintenanceState = Schema.decodeUnknownSync(ThreadMaintenanceState);
150
+ const encodeMaintenanceState = Schema.encodeSync(ThreadMaintenanceState);
151
+ const initialMaintenanceState = () => ThreadMaintenanceState.make({
152
+ schemaVersion: 1,
153
+ dirty: 1n,
154
+ processed: 0n,
155
+ nonterminal: 0
156
+ });
157
+ const readMaintenanceState = async (transaction) => {
158
+ const encoded = await transaction.get(MAINTENANCE_STATE_KEY);
159
+ return encoded === void 0 ? {
160
+ state: initialMaintenanceState(),
161
+ initialized: false
162
+ } : {
163
+ state: decodeMaintenanceState(encoded),
164
+ initialized: true
165
+ };
166
+ };
167
+ const ensureTransactionAlarmBy = async (transaction, deadline) => {
168
+ const scheduled = await transaction.getAlarm();
169
+ if (scheduled === null || scheduled > deadline) await transaction.setAlarm(deadline);
170
+ };
171
+ const stableExternalWait = (snapshot, reports) => {
172
+ const decision = reports.get(snapshot.submissionId)?.decision._tag;
173
+ if (decision === "SettleAborted") return false;
174
+ switch (snapshot.state) {
175
+ case "suspended":
176
+ case "joined": return true;
177
+ case "unknown": return decision === "AwaitUnknownResolution" || decision === "MarkUnknown";
178
+ case "admitted": return reports.get(snapshot.submissionId)?.decision._tag === "AwaitParentEstablishment";
179
+ case "input-applied":
180
+ case "joining":
181
+ case "ready":
182
+ case "running":
183
+ case "settled":
184
+ case "terminalizing": return false;
185
+ }
186
+ };
187
+ /**
188
+ * Shared prearm/acknowledgement boundary for ingress and runtime-owned producers.
189
+ * `ThreadObject.layer` provides this same instance in its Services. Rebuilt runtime/maintenance
190
+ * Layers must reuse that instance; a second gate cannot observe the native producers' activity.
191
+ */
192
+ var ThreadMutationGate = class ThreadMutationGate extends Context.Service()("@effect-agent/platform-cloudflare/internal/ThreadMutationGate") {
193
+ static layer = Layer.effect(this)(Effect.gen(function* () {
194
+ const { ctx } = yield* DurableObjectContext;
195
+ const config = yield* CloudflareDurableRuntimeConfig;
196
+ const failpoint = yield* ThreadMaintenanceFailpoint;
197
+ const activeMutations = yield* Ref.make(0);
198
+ const generationGate = yield* Semaphore.make(1);
199
+ const minimumAlarmDelay = Math.max(1, Math.ceil(config.alarmBackoffBase / 2));
200
+ const runTransaction = yield* makeStorageOperation;
201
+ const beginMutation = Effect.fn("ThreadMaintenance.beginMutation")(function* (invalidatesRecovery) {
202
+ yield* failpoint.hit("maintenance:dirty:before");
203
+ const now = yield* Clock.currentTimeMillis;
204
+ yield* runTransaction("advance maintenance generation", () => ctx.storage.transaction(async (transaction) => {
205
+ const { state, initialized } = await readMaintenanceState(transaction);
206
+ const next = ThreadMaintenanceState.make({
207
+ ...state,
208
+ dirty: state.dirty + (invalidatesRecovery ? 1n : 0n)
209
+ });
210
+ if (invalidatesRecovery || !initialized) await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(next));
211
+ await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);
212
+ }));
213
+ yield* failpoint.hit("maintenance:dirty:after");
214
+ yield* Ref.update(activeMutations, (active) => active + 1);
215
+ });
216
+ const endMutation = generationGate.withPermit(Ref.update(activeMutations, (active) => Math.max(0, active - 1)));
217
+ const withMutation = (body, options) => Effect.acquireUseRelease(generationGate.withPermit(beginMutation(options?.invalidatesRecovery ?? true)), () => failpoint.hit("maintenance:mutation:armed").pipe(Effect.andThen(body), Effect.tap(() => failpoint.hit("maintenance:mutation:finished"))), () => endMutation);
218
+ return ThreadMutationGate.of({
219
+ withMutation,
220
+ withSnapshot: (body) => generationGate.withPermit(Effect.flatMap(Ref.get(activeMutations), body))
221
+ });
222
+ }));
223
+ };
224
+ /**
225
+ * Incremental, quiescent maintenance over a durable dirty/processed generation (issue #93).
226
+ *
227
+ * `pass` = generation snapshot/pre-arm → recovery → one head Attempt → generation acknowledgement:
228
+ *
229
+ * 1. One storage transaction reads dirty/processed and re-arms before work. A caught-up forced
230
+ * alarm takes an O(1) path without recovery, ledger scans, or canonical-history reads.
231
+ * 2. Recovery strictly precedes a new claim. One head Attempt advances the lane and requests
232
+ * a safe yield after ten minutes. The whole event has a fourteen-minute cooperative timeout.
233
+ * 3. The final transaction acknowledges only the generation observed at pass start. A racing
234
+ * mutation therefore remains `dirty > processed` and retains its atomically-established alarm.
235
+ * 4. Stable external waits acknowledge and clear. Autonomous retry, indeterminate, and lease
236
+ * recovery states leave their generation dirty and retain bounded backoff rearming.
237
+ */
238
+ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effect-agent/platform-cloudflare/ThreadMaintenance") {
239
+ static layer = Layer.effect(ThreadMaintenance)(Effect.gen(function* () {
240
+ const runtime = yield* DurableAgentRuntime;
241
+ const ledger = yield* SubmissionLedger;
242
+ const alarm = yield* DurableAlarmService;
243
+ const config = yield* CloudflareDurableRuntimeConfig;
244
+ const { ctx } = yield* DurableObjectContext;
245
+ const failpoint = yield* ThreadMaintenanceFailpoint;
246
+ /**
247
+ * Consecutive no-progress passes — an in-memory CACHE, not state: a fresh incarnation
248
+ * restarts at zero and merely re-arms sooner than a long-lived one would have.
249
+ */
250
+ const stalls = yield* Ref.make(0);
251
+ const mutations = yield* ThreadMutationGate;
252
+ const publication = yield* ThreadPublication;
253
+ const projection = yield* ThreadProjectionMaintenance;
254
+ const messages = yield* ThreadMessageDelivery;
255
+ const host = yield* ThreadHostMaintenance;
256
+ const projectionDeadline = projection.pendingDeadline.pipe(Effect.catchCauseIf((cause) => !Cause.hasInterrupts(cause), (cause) => Effect.logError("Thread projection deadline unavailable", cause).pipe(Effect.as(Option.some(0)))));
257
+ const pendingDeadline = Effect.gen(function* () {
258
+ return earliestDeadline(earliestDeadline(yield* publication.pendingDeadline, yield* messages.pendingDeadline), earliestDeadline(yield* projectionDeadline, yield* host.pendingDeadline));
259
+ });
260
+ const maintenancePassGate = yield* Semaphore.make(1);
261
+ const minimumAlarmDelay = Math.max(1, Math.ceil(config.alarmBackoffBase / 2));
262
+ const runTransaction = yield* makeStorageOperation;
263
+ const ensureAlarm = Effect.fn("ThreadMaintenance.ensureAlarm")(function* () {
264
+ yield* failpoint.hit("maintenance:ensure:before");
265
+ const now = yield* Clock.currentTimeMillis;
266
+ yield* runTransaction("ensure maintenance alarm", () => ctx.storage.transaction(async (transaction) => {
267
+ const { state, initialized } = await readMaintenanceState(transaction);
268
+ if (!initialized) await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(state));
269
+ if (state.dirty > state.processed) await ensureTransactionAlarmBy(transaction, now + config.wakeScanInterval);
270
+ }));
271
+ const deadline = yield* pendingDeadline;
272
+ if (Option.isSome(deadline)) yield* runTransaction("ensure publication alarm", () => ctx.storage.transaction((transaction) => ensureTransactionAlarmBy(transaction, Math.max(now + minimumAlarmDelay, deadline.value))));
273
+ yield* failpoint.hit("maintenance:ensure:after");
274
+ });
275
+ const beginPass = Effect.fn("ThreadMaintenance.beginPass")(function* () {
276
+ yield* failpoint.hit("maintenance:begin:before");
277
+ const now = yield* Clock.currentTimeMillis;
278
+ const result = yield* runTransaction("begin maintenance pass", () => ctx.storage.transaction(async (transaction) => {
279
+ const { state, initialized } = await readMaintenanceState(transaction);
280
+ if (!initialized) await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(state));
281
+ if (state.processed >= state.dirty) {
282
+ await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);
283
+ return {
284
+ _tag: "CaughtUp",
285
+ nonterminal: state.nonterminal
286
+ };
287
+ }
288
+ await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);
289
+ return {
290
+ _tag: "Actionable",
291
+ generation: state.dirty,
292
+ nonterminal: state.nonterminal
293
+ };
294
+ }));
295
+ yield* failpoint.hit("maintenance:begin:after");
296
+ return result;
297
+ });
298
+ const rearmDelay = Effect.fn("ThreadMaintenance.rearmDelay")(function* (progressed) {
299
+ const priorStalls = yield* Ref.getAndUpdate(stalls, (count) => progressed ? 0 : count + 1);
300
+ if (progressed) return config.alarmBackoffBase;
301
+ const exponent = Math.min(priorStalls, 30);
302
+ const backoff = Math.min(config.alarmBackoffCap, config.alarmBackoffBase * 2 ** exponent);
303
+ const jitter = yield* Random.next;
304
+ const jittered = Math.ceil(backoff / 2 + backoff / 2 * jitter);
305
+ return Math.min(jittered, config.wakeScanInterval);
306
+ });
307
+ const pass = Effect.fn("ThreadMaintenance.pass")(function* (yieldAfter) {
308
+ const annotate = (report) => Effect.annotateCurrentSpan({
309
+ phase: report.phase,
310
+ recovered: report.recovered,
311
+ settled: report.settled,
312
+ nonterminal: report.nonterminal,
313
+ alarm: report.alarm
314
+ }).pipe(Effect.as(report));
315
+ const started = yield* mutations.withSnapshot((activeAtStart) => Effect.gen(function* () {
316
+ const generation = yield* beginPass();
317
+ if (generation._tag === "Actionable" && activeAtStart === 0) yield* publication.prepareGeneration(generation.generation);
318
+ return {
319
+ ...generation,
320
+ activeAtStart
321
+ };
322
+ }));
323
+ const deliveryFinished = yield* Deferred.make();
324
+ const delivery = yield* Effect.forkChild(messages.drainUntil?.(deliveryFinished) ?? messages.drain);
325
+ const hostWork = yield* Effect.forkChild(host.drainUntil(deliveryFinished));
326
+ const finishDelivery = Deferred.succeed(deliveryFinished, void 0).pipe(Effect.andThen(Fiber.awaitAll([delivery, hostWork])), Effect.flatMap((outcomes) => Effect.forEach(outcomes, (outcome) => outcome, { discard: true })));
327
+ const projected = yield* Effect.exit(drainDue.pipe(Effect.provideService(ThreadProjectionMaintenance, projection)));
328
+ if (Exit.isFailure(projected) && Cause.hasInterrupts(projected.cause)) return yield* Effect.failCause(projected.cause);
329
+ const deadline = yield* publication.pendingDeadline;
330
+ if (started._tag === "Actionable" || Option.isSome(deadline) && deadline.value <= (yield* Clock.currentTimeMillis)) yield* publication.drain;
331
+ const pending = yield* publication.pendingDeadline;
332
+ if (started._tag === "CaughtUp" || Option.isSome(pending)) {
333
+ yield* finishDelivery;
334
+ if (Exit.isFailure(projected)) return yield* Effect.failCause(projected.cause);
335
+ yield* failpoint.hit("maintenance:finish:before");
336
+ const disposition = yield* mutations.withSnapshot((active) => Effect.gen(function* () {
337
+ const latest = yield* pendingDeadline;
338
+ const now = yield* Clock.currentTimeMillis;
339
+ return yield* runTransaction("finish publication pass", () => ctx.storage.transaction(async (transaction) => {
340
+ const { state } = await readMaintenanceState(transaction);
341
+ const nativeDeadline = active > 0 || state.dirty > state.processed ? now + config.wakeScanInterval : Infinity;
342
+ const next = Option.isSome(latest) ? Math.min(nativeDeadline, latest.value) : nativeDeadline;
343
+ if (Number.isFinite(next)) {
344
+ await transaction.setAlarm(Math.max(now + minimumAlarmDelay, next));
345
+ return "rearmed";
346
+ }
347
+ await transaction.deleteAlarm();
348
+ return "cleared";
349
+ }));
350
+ }));
351
+ yield* failpoint.hit("maintenance:finish:after");
352
+ return yield* annotate(MaintenancePassReport.make({
353
+ phase: "caught-up",
354
+ recovered: 0,
355
+ settled: 0,
356
+ nonterminal: started.nonterminal,
357
+ alarm: disposition
358
+ }));
359
+ }
360
+ const recovered = yield* runtime.runRecovery;
361
+ const reports = new Map(recovered.map((report) => [report.submissionId, report]));
362
+ const current = yield* Stream.runCollect(ledger.scanNonterminal);
363
+ const heads = /* @__PURE__ */ new Map();
364
+ for (const row of current) if (!heads.has(row.threadId)) heads.set(row.threadId, row);
365
+ const eligible = [...heads.values()].filter((head) => !stableExternalWait(head, reports)).map((head) => head.threadId).sort();
366
+ let selected = eligible[0];
367
+ if (heads.size > 1 && selected !== void 0) {
368
+ yield* failpoint.hit("maintenance:select:before");
369
+ selected = yield* runTransaction("select maintenance lane", () => ctx.storage.transaction(async (transaction) => {
370
+ const { state } = await readMaintenanceState(transaction);
371
+ const next = eligible.find((threadId) => state.lastServedThreadId === void 0 || threadId > state.lastServedThreadId) ?? eligible[0];
372
+ if (next !== void 0) await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(ThreadMaintenanceState.make({
373
+ ...state,
374
+ lastServedThreadId: next
375
+ })));
376
+ return next;
377
+ }));
378
+ yield* failpoint.hit("maintenance:select:after");
379
+ }
380
+ const settlement = selected === void 0 ? Option.none() : yield* runtime.processThreadHead(selected, { yieldAfter });
381
+ yield* finishDelivery;
382
+ if (Exit.isFailure(projected)) return yield* Effect.failCause(projected.cause);
383
+ const remaining = yield* Stream.runCollect(ledger.scanNonterminal);
384
+ const waitingHeads = /* @__PURE__ */ new Map();
385
+ const autonomous = remaining.some((snapshot) => {
386
+ const headWaiting = waitingHeads.get(snapshot.threadId);
387
+ if (headWaiting === void 0) waitingHeads.set(snapshot.threadId, stableExternalWait(snapshot, reports));
388
+ if (headWaiting === true && snapshot.state === "ready" && reports.get(snapshot.submissionId)?.decision._tag === "ApplyInput") return false;
389
+ return !stableExternalWait(snapshot, reports);
390
+ });
391
+ const progressed = Option.isSome(settlement) || recovered.some((report) => report.disposition === "repaired");
392
+ const delay = autonomous ? yield* rearmDelay(progressed) : 0;
393
+ const now = yield* Clock.currentTimeMillis;
394
+ yield* failpoint.hit("maintenance:finish:before");
395
+ const alarmDisposition = yield* mutations.withSnapshot((active) => Effect.gen(function* () {
396
+ const publicationDeadline = yield* pendingDeadline;
397
+ return yield* runTransaction("finish maintenance pass", () => ctx.storage.transaction(async (transaction) => {
398
+ const { state } = await readMaintenanceState(transaction);
399
+ const processed = autonomous || started.activeAtStart > 0 || active > 0 ? state.processed : state.processed > started.generation ? state.processed : started.generation;
400
+ const next = ThreadMaintenanceState.make({
401
+ ...state,
402
+ processed,
403
+ nonterminal: remaining.length
404
+ });
405
+ await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(next));
406
+ if (autonomous) {
407
+ await transaction.setAlarm(Option.isSome(publicationDeadline) ? Math.max(now + minimumAlarmDelay, Math.min(now + delay, publicationDeadline.value)) : now + delay);
408
+ return "rearmed";
409
+ }
410
+ if (started.activeAtStart > 0 || active > 0 || next.dirty > next.processed) {
411
+ await ensureTransactionAlarmBy(transaction, Option.isSome(publicationDeadline) ? Math.max(now + minimumAlarmDelay, Math.min(now + config.wakeScanInterval, publicationDeadline.value)) : now + config.wakeScanInterval);
412
+ return "rearmed";
413
+ }
414
+ if (Option.isSome(publicationDeadline)) {
415
+ await transaction.setAlarm(Math.max(now + minimumAlarmDelay, publicationDeadline.value));
416
+ return "rearmed";
417
+ }
418
+ await transaction.deleteAlarm();
419
+ return "cleared";
420
+ }));
421
+ }));
422
+ yield* failpoint.hit("maintenance:finish:after");
423
+ if (alarmDisposition === "cleared") yield* Ref.set(stalls, 0);
424
+ return yield* annotate(MaintenancePassReport.make({
425
+ phase: "actionable",
426
+ recovered: recovered.length,
427
+ settled: Option.isSome(settlement) ? 1 : 0,
428
+ nonterminal: remaining.length,
429
+ alarm: alarmDisposition
430
+ }));
431
+ });
432
+ return ThreadMaintenance.of({
433
+ pass: Effect.gen(function* () {
434
+ const yieldAfter = DateTime.makeUnsafe((yield* Clock.currentTimeMillis) + 6e5);
435
+ return yield* alarm.withWakesDeferred(maintenancePassGate.withPermit(pass(yieldAfter)));
436
+ }).pipe(Effect.timeoutOrElse({
437
+ duration: "14 minutes",
438
+ orElse: () => DurableAlarmError.make({
439
+ operation: "maintenance pass deadline",
440
+ message: "The maintenance event exceeded its 14 minute deadline; durable recovery remains pending"
441
+ })
442
+ })),
443
+ ensureAlarm: mutations.withSnapshot(() => ensureAlarm()),
444
+ withMutation: (body) => mutations.withMutation(body.pipe(Effect.tap(() => publishCommitted.pipe(Effect.provideService(ThreadPublication, publication)))))
445
+ });
446
+ }));
447
+ };
448
+ //#endregion
449
+ export { DurableAlarmError, DurableAlarmService, MaintenancePassReport, ThreadHostMaintenance, ThreadMaintenance, ThreadMaintenanceFailpoint, ThreadMessageDelivery, ThreadMutationGate, ThreadPublication, publishCommitted, Alarm_exports as t };
450
+
451
+ //# sourceMappingURL=Alarm.mjs.map