@effect-agent/platform-cloudflare 0.1.0-beta.94 → 0.1.0-beta.95
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.
- package/dist/Alarm.d.mts +37 -18
- package/dist/Alarm.mjs +62 -29
- package/dist/Alarm.mjs.map +1 -1
- package/dist/{CloudflareConfig-f3CqTel1.d.mts → CloudflareConfig-D472JOQA.d.mts} +8 -3
- package/dist/CloudflareConfig.d.mts +2 -2
- package/dist/CloudflareConfig.mjs +7 -1
- package/dist/CloudflareConfig.mjs.map +1 -1
- package/dist/CloudflareThreadClient.d.mts +34 -34
- package/dist/{ThreadObject-8LfGzzz4.d.mts → ThreadObject-CZxuDWOb.d.mts} +37 -35
- package/dist/{ThreadObject-DvRG0dDz.mjs → ThreadObject-DXavwaU-.mjs} +34 -16
- package/dist/ThreadObject-DXavwaU-.mjs.map +1 -0
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/ThreadObject.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/Alarm.ts +124 -50
- package/src/CloudflareConfig.ts +6 -0
- package/src/internal/layers.ts +7 -2
- package/src/internal/message-delivery.ts +69 -40
- package/dist/ThreadObject-DvRG0dDz.mjs.map +0 -1
package/dist/Alarm.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DurableObjectContext } from "./CloudflareBindings.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { Cause, Context,
|
|
2
|
+
import { s as CloudflareDurableRuntimeConfig } from "./CloudflareConfig-D472JOQA.mjs";
|
|
3
|
+
import { Cause, Context, DateTime, Effect, Layer, Option, Schema, Scope } from "effect";
|
|
4
4
|
import { DurableBindingFailure } from "effect-agent/agent-registration";
|
|
5
5
|
import { DurableAgentRuntime, DurableWorkerFailure } from "effect-agent/durable-agent-runtime";
|
|
6
6
|
import { SubmissionLedger } from "effect-agent/submission-ledger";
|
|
@@ -92,6 +92,8 @@ declare class ThreadMaintenanceFailpoint extends ThreadMaintenanceFailpoint_base
|
|
|
92
92
|
* `invalidate`, `prepareGeneration` and `pendingDeadline` must be bounded local operations.
|
|
93
93
|
* `prepareGeneration` durably invalidates a scan only when its generation changes; repeated
|
|
94
94
|
* calls must preserve partial scan progress. It runs with no source mutation in flight.
|
|
95
|
+
* Use this gate only for publication required before dependent native execution. Independent
|
|
96
|
+
* UI relays and outboxes belong to ThreadHostMaintenance.
|
|
95
97
|
* `drain` performs bounded delivery and persists retries before returning. A pending deadline
|
|
96
98
|
* defers runtime recovery/Attempts, allowing committed host publications to drain first.
|
|
97
99
|
* Unexpected hook failures leave the prearmed generation for retry. Hooks acquire per-call
|
|
@@ -110,33 +112,46 @@ declare class ThreadPublication extends ThreadPublication_base {
|
|
|
110
112
|
static readonly layer: Layer.Layer<ThreadPublication, never, never>;
|
|
111
113
|
}
|
|
112
114
|
/**
|
|
113
|
-
* Host-assembled message recovery
|
|
114
|
-
*
|
|
115
|
-
*
|
|
115
|
+
* Host-assembled native message recovery. The driver bounds each actual Claim and persists its
|
|
116
|
+
* timeout/retry before this pump returns. Do not add a second timer starting at batch selection:
|
|
117
|
+
* local Claim setup may take time, and expiration still owes the driver's local retry commit.
|
|
116
118
|
*/
|
|
117
119
|
declare const ThreadMessageDelivery: Context.Reference<{
|
|
118
|
-
readonly
|
|
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>;
|
|
120
|
+
readonly drainUntil: (sourceFinished: Effect.Effect<void>, dispatchUntil: DateTime.Utc) => Effect.Effect<void, DurableAlarmError, Scope.Scope>;
|
|
121
121
|
readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;
|
|
122
122
|
}>;
|
|
123
123
|
/**
|
|
124
|
-
* Application obligations sharing this Object's alarm.
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
124
|
+
* Application obligations sharing this Object's alarm. Admit one initial external wave even on
|
|
125
|
+
* a caught-up pass, then respond to wakes while native work runs. sourceFinished closes only
|
|
126
|
+
* admission of NEW external waves. Keep local admission/hub subscriptions in the supplied event
|
|
127
|
+
* Scope until maintenance tears it down; do not scope them to sourceFinished or to this Effect.
|
|
128
|
+
* No deadline sleeps or automatic retry loops. Return after already-admitted waves finish.
|
|
129
|
+
*
|
|
130
|
+
* Declare a finite whole-wave allowance (1..300000ms): maximum for parallel lanes, sum for
|
|
131
|
+
* sequential operations. Admit a wave only if its allowance fits before dispatchUntil. Later
|
|
132
|
+
* arrivals cannot renew the retirement window. Maintenance bounds the join after sourceFinished,
|
|
133
|
+
* interrupts and joins event Scope, then reads local deadlines under the mutation gate.
|
|
134
|
+
*
|
|
135
|
+
* Setup and pendingDeadline are bounded local operations. Persist claims/envelopes before
|
|
136
|
+
* dispatch; interrupted waits leave exact retries/receipts recoverable. Cancellation is local,
|
|
137
|
+
* not remote rollback. Retry accounting is unchanged. Network waits/finalizers must be
|
|
138
|
+
* interruptible; short local atomic commits may be uninterruptible. Hooks never write the raw
|
|
139
|
+
* alarm slot. Required native publication gates belong to ThreadPublication.
|
|
130
140
|
*/
|
|
131
141
|
declare const ThreadHostMaintenance: Context.Reference<{
|
|
142
|
+
readonly dispatchTimeoutMillis: number;
|
|
143
|
+
readonly drainUntil: (sourceFinished: Effect.Effect<void>, dispatchUntil: DateTime.Utc) => Effect.Effect<void, DurableAlarmError, Scope.Scope>;
|
|
132
144
|
readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;
|
|
133
|
-
readonly drainUntil: (finished: Deferred.Deferred<void>) => Effect.Effect<void, DurableAlarmError>;
|
|
134
145
|
}>;
|
|
135
146
|
/** @internal A committed source operation must not become a failed operation because delivery failed. */
|
|
136
147
|
declare const publishCommitted: Effect.Effect<void, never, ThreadPublication>;
|
|
137
148
|
declare const ThreadMutationGate_base: Context.ServiceClass<ThreadMutationGate, "@effect-agent/platform-cloudflare/internal/ThreadMutationGate", {
|
|
138
149
|
readonly withMutation: <A, E, R>(body: Effect.Effect<A, E, R>,
|
|
139
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Native admission, approval, abort and unknown resolution keep the default true.
|
|
152
|
+
* Projection/relay/reply receipt-only bookkeeping must use false: its local durable
|
|
153
|
+
* pendingDeadline owns scheduling without creating native recovery debt.
|
|
154
|
+
*/
|
|
140
155
|
options?: {
|
|
141
156
|
readonly invalidatesRecovery: boolean;
|
|
142
157
|
}) => Effect.Effect<A, E | DurableAlarmError, R>;
|
|
@@ -175,9 +190,13 @@ declare const ThreadMaintenance_base: Context.ServiceClass<ThreadMaintenance, "@
|
|
|
175
190
|
* alarm takes an O(1) path without recovery, ledger scans, or canonical-history reads.
|
|
176
191
|
* 2. Recovery strictly precedes a new claim. One head Attempt advances the lane and requests
|
|
177
192
|
* a safe yield after ten minutes. The whole event has a fourteen-minute cooperative timeout.
|
|
178
|
-
* 3.
|
|
193
|
+
* 3. Auxiliary dispatch and listeners belong to the event Scope. After native completion,
|
|
194
|
+
* stop new external waves, join native delivery through its driver-owned Claim deadline,
|
|
195
|
+
* and bound host/backfill work by explicit allowances before closing and joining Scope.
|
|
196
|
+
* Ordinary auxiliary setup failures are reported after the one native opportunity.
|
|
197
|
+
* 4. The final transaction acknowledges only the generation observed at pass start. A racing
|
|
179
198
|
* mutation therefore remains `dirty > processed` and retains its atomically-established alarm.
|
|
180
|
-
*
|
|
199
|
+
* 5. Stable external waits acknowledge and clear. Autonomous retry, indeterminate, and lease
|
|
181
200
|
* recovery states leave their generation dirty and retain bounded backoff rearming.
|
|
182
201
|
*/
|
|
183
202
|
declare class ThreadMaintenance extends ThreadMaintenance_base {
|
package/dist/Alarm.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
2
|
import { DurableObjectContext } from "./CloudflareBindings.mjs";
|
|
3
|
-
import { CloudflareDurableRuntimeConfig } from "./CloudflareConfig.mjs";
|
|
3
|
+
import { AuxiliaryDispatchMillis, CloudflareDurableRuntimeConfig } from "./CloudflareConfig.mjs";
|
|
4
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";
|
|
5
|
+
import { Cause, Clock, Context, DateTime, Deferred, Effect, Exit, Fiber, Layer, Option, Random, Ref, Schema, Scope, Semaphore, Stream } from "effect";
|
|
6
6
|
import "effect-agent/agent-registration";
|
|
7
7
|
import { DurableAgentRuntime } from "effect-agent/durable-agent-runtime";
|
|
8
8
|
import { ThreadId } from "effect-agent/identifiers";
|
|
@@ -109,25 +109,36 @@ var ThreadPublication = class extends Context.Service()("@effect-agent/platform-
|
|
|
109
109
|
});
|
|
110
110
|
};
|
|
111
111
|
/**
|
|
112
|
-
* Host-assembled message recovery
|
|
113
|
-
*
|
|
114
|
-
*
|
|
112
|
+
* Host-assembled native message recovery. The driver bounds each actual Claim and persists its
|
|
113
|
+
* timeout/retry before this pump returns. Do not add a second timer starting at batch selection:
|
|
114
|
+
* local Claim setup may take time, and expiration still owes the driver's local retry commit.
|
|
115
115
|
*/
|
|
116
116
|
const ThreadMessageDelivery = Context.Reference("@effect-agent/platform-cloudflare/ThreadMessageDelivery", { defaultValue: () => ({
|
|
117
|
-
|
|
117
|
+
drainUntil: () => Effect.void,
|
|
118
118
|
pendingDeadline: Effect.succeed(Option.none())
|
|
119
119
|
}) });
|
|
120
120
|
/**
|
|
121
|
-
* Application obligations sharing this Object's alarm.
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
121
|
+
* Application obligations sharing this Object's alarm. Admit one initial external wave even on
|
|
122
|
+
* a caught-up pass, then respond to wakes while native work runs. sourceFinished closes only
|
|
123
|
+
* admission of NEW external waves. Keep local admission/hub subscriptions in the supplied event
|
|
124
|
+
* Scope until maintenance tears it down; do not scope them to sourceFinished or to this Effect.
|
|
125
|
+
* No deadline sleeps or automatic retry loops. Return after already-admitted waves finish.
|
|
126
|
+
*
|
|
127
|
+
* Declare a finite whole-wave allowance (1..300000ms): maximum for parallel lanes, sum for
|
|
128
|
+
* sequential operations. Admit a wave only if its allowance fits before dispatchUntil. Later
|
|
129
|
+
* arrivals cannot renew the retirement window. Maintenance bounds the join after sourceFinished,
|
|
130
|
+
* interrupts and joins event Scope, then reads local deadlines under the mutation gate.
|
|
131
|
+
*
|
|
132
|
+
* Setup and pendingDeadline are bounded local operations. Persist claims/envelopes before
|
|
133
|
+
* dispatch; interrupted waits leave exact retries/receipts recoverable. Cancellation is local,
|
|
134
|
+
* not remote rollback. Retry accounting is unchanged. Network waits/finalizers must be
|
|
135
|
+
* interruptible; short local atomic commits may be uninterruptible. Hooks never write the raw
|
|
136
|
+
* alarm slot. Required native publication gates belong to ThreadPublication.
|
|
127
137
|
*/
|
|
128
138
|
const ThreadHostMaintenance = Context.Reference("@effect-agent/platform-cloudflare/ThreadHostMaintenance", { defaultValue: () => ({
|
|
129
|
-
|
|
130
|
-
drainUntil: () => Effect.void
|
|
139
|
+
dispatchTimeoutMillis: 1,
|
|
140
|
+
drainUntil: () => Effect.void,
|
|
141
|
+
pendingDeadline: Effect.succeed(Option.none())
|
|
131
142
|
}) });
|
|
132
143
|
const earliestDeadline = (left, right) => Option.isSome(left) ? Option.isSome(right) ? Option.some(Math.min(left.value, right.value)) : left : right;
|
|
133
144
|
/** @internal A committed source operation must not become a failed operation because delivery failed. */
|
|
@@ -230,9 +241,13 @@ var ThreadMutationGate = class ThreadMutationGate extends Context.Service()("@ef
|
|
|
230
241
|
* alarm takes an O(1) path without recovery, ledger scans, or canonical-history reads.
|
|
231
242
|
* 2. Recovery strictly precedes a new claim. One head Attempt advances the lane and requests
|
|
232
243
|
* a safe yield after ten minutes. The whole event has a fourteen-minute cooperative timeout.
|
|
233
|
-
* 3.
|
|
244
|
+
* 3. Auxiliary dispatch and listeners belong to the event Scope. After native completion,
|
|
245
|
+
* stop new external waves, join native delivery through its driver-owned Claim deadline,
|
|
246
|
+
* and bound host/backfill work by explicit allowances before closing and joining Scope.
|
|
247
|
+
* Ordinary auxiliary setup failures are reported after the one native opportunity.
|
|
248
|
+
* 4. The final transaction acknowledges only the generation observed at pass start. A racing
|
|
234
249
|
* mutation therefore remains `dirty > processed` and retains its atomically-established alarm.
|
|
235
|
-
*
|
|
250
|
+
* 5. Stable external waits acknowledge and clear. Autonomous retry, indeterminate, and lease
|
|
236
251
|
* recovery states leave their generation dirty and retain bounded backoff rearming.
|
|
237
252
|
*/
|
|
238
253
|
var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effect-agent/platform-cloudflare/ThreadMaintenance") {
|
|
@@ -304,7 +319,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
304
319
|
const jittered = Math.ceil(backoff / 2 + backoff / 2 * jitter);
|
|
305
320
|
return Math.min(jittered, config.wakeScanInterval);
|
|
306
321
|
});
|
|
307
|
-
const pass = Effect.fn("ThreadMaintenance.pass")(function* (yieldAfter) {
|
|
322
|
+
const pass = Effect.fn("ThreadMaintenance.pass")(function* (yieldAfter, dispatchUntil) {
|
|
308
323
|
const annotate = (report) => Effect.annotateCurrentSpan({
|
|
309
324
|
phase: report.phase,
|
|
310
325
|
recovered: report.recovered,
|
|
@@ -320,18 +335,35 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
320
335
|
activeAtStart
|
|
321
336
|
};
|
|
322
337
|
}));
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
|
|
338
|
+
const auxiliaryScope = yield* Effect.acquireRelease(Scope.make("parallel"), (scope, exit) => Scope.close(scope, exit));
|
|
339
|
+
const sourceFinished = yield* Deferred.make();
|
|
340
|
+
const finished = Deferred.await(sourceFinished);
|
|
341
|
+
const deliveryFiber = yield* Effect.forkIn(Scope.provide(auxiliaryScope)(messages.drainUntil(finished, dispatchUntil)), auxiliaryScope);
|
|
342
|
+
const hostFiber = yield* Effect.forkIn(Scope.provide(auxiliaryScope)(Effect.gen(function* () {
|
|
343
|
+
yield* Schema.decodeEffect(AuxiliaryDispatchMillis)(host.dispatchTimeoutMillis).pipe(Effect.mapError((cause) => DurableAlarmError.make({
|
|
344
|
+
operation: "host dispatch allowance",
|
|
345
|
+
message: "Declare an integer whole-wave allowance between 1 and 300000 milliseconds",
|
|
346
|
+
cause
|
|
347
|
+
})));
|
|
348
|
+
yield* host.drainUntil(finished, dispatchUntil);
|
|
349
|
+
})), auxiliaryScope);
|
|
350
|
+
const backfill = yield* Effect.forkIn(drainDue.pipe(Effect.provideService(ThreadProjectionMaintenance, projection), Effect.timeoutOption(config.projectionDispatchTimeoutMillis)), auxiliaryScope);
|
|
351
|
+
const finishAuxiliary = Effect.gen(function* () {
|
|
352
|
+
yield* Deferred.succeed(sourceFinished, void 0);
|
|
353
|
+
const remaining = Math.max(1, DateTime.toEpochMillis(dispatchUntil) - (yield* Clock.currentTimeMillis));
|
|
354
|
+
const hostJoin = yield* Effect.forkIn(Fiber.join(hostFiber).pipe(Effect.timeoutOption(Math.min(host.dispatchTimeoutMillis, remaining)), Effect.tap((result) => Effect.annotateCurrentSpan({ "host.timedOut": Option.isNone(result) }))), auxiliaryScope);
|
|
355
|
+
yield* Fiber.joinAll([
|
|
356
|
+
deliveryFiber,
|
|
357
|
+
hostJoin,
|
|
358
|
+
backfill
|
|
359
|
+
]);
|
|
360
|
+
yield* Scope.close(auxiliaryScope, Exit.void);
|
|
361
|
+
});
|
|
329
362
|
const deadline = yield* publication.pendingDeadline;
|
|
330
363
|
if (started._tag === "Actionable" || Option.isSome(deadline) && deadline.value <= (yield* Clock.currentTimeMillis)) yield* publication.drain;
|
|
331
364
|
const pending = yield* publication.pendingDeadline;
|
|
332
365
|
if (started._tag === "CaughtUp" || Option.isSome(pending)) {
|
|
333
|
-
yield*
|
|
334
|
-
if (Exit.isFailure(projected)) return yield* Effect.failCause(projected.cause);
|
|
366
|
+
yield* finishAuxiliary;
|
|
335
367
|
yield* failpoint.hit("maintenance:finish:before");
|
|
336
368
|
const disposition = yield* mutations.withSnapshot((active) => Effect.gen(function* () {
|
|
337
369
|
const latest = yield* pendingDeadline;
|
|
@@ -378,8 +410,7 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
378
410
|
yield* failpoint.hit("maintenance:select:after");
|
|
379
411
|
}
|
|
380
412
|
const settlement = selected === void 0 ? Option.none() : yield* runtime.processThreadHead(selected, { yieldAfter });
|
|
381
|
-
yield*
|
|
382
|
-
if (Exit.isFailure(projected)) return yield* Effect.failCause(projected.cause);
|
|
413
|
+
yield* finishAuxiliary;
|
|
383
414
|
const remaining = yield* Stream.runCollect(ledger.scanNonterminal);
|
|
384
415
|
const waitingHeads = /* @__PURE__ */ new Map();
|
|
385
416
|
const autonomous = remaining.some((snapshot) => {
|
|
@@ -431,8 +462,10 @@ var ThreadMaintenance = class ThreadMaintenance extends Context.Service()("@effe
|
|
|
431
462
|
});
|
|
432
463
|
return ThreadMaintenance.of({
|
|
433
464
|
pass: Effect.gen(function* () {
|
|
434
|
-
const
|
|
435
|
-
|
|
465
|
+
const now = yield* Clock.currentTimeMillis;
|
|
466
|
+
const yieldAfter = DateTime.makeUnsafe(now + 6e5);
|
|
467
|
+
const dispatchUntil = DateTime.makeUnsafe(now + 84e4);
|
|
468
|
+
return yield* alarm.withWakesDeferred(maintenancePassGate.withPermit(Effect.scoped(pass(yieldAfter, dispatchUntil))));
|
|
436
469
|
}).pipe(Effect.timeoutOrElse({
|
|
437
470
|
duration: "14 minutes",
|
|
438
471
|
orElse: () => DurableAlarmError.make({
|
package/dist/Alarm.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alarm.mjs","names":[],"sources":["../src/Alarm.ts"],"sourcesContent":["import {\n Cause,\n Clock,\n Context,\n DateTime,\n Deferred,\n Effect,\n Exit,\n Fiber,\n Layer,\n Option,\n Random,\n Ref,\n Schema,\n Semaphore,\n Stream,\n} from \"effect\";\nimport { type DurableBindingFailure } from \"effect-agent/agent-registration\";\nimport {\n DurableAgentRuntime,\n type DurableWorkerFailure,\n type RecoveryReport,\n} from \"effect-agent/durable-agent-runtime\";\nimport { ThreadId } from \"effect-agent/identifiers\";\nimport { SubmissionLedger, type SubmissionSnapshot } from \"effect-agent/submission-ledger\";\nimport {\n ThreadProjectionMaintenance,\n drainDue,\n type ThreadProjectionError,\n} from \"effect-agent/thread-projection-maintenance\";\nimport { SqlClient } from \"effect/unstable/sql/SqlClient\";\n\nimport { DurableObjectContext } from \"./CloudflareBindings.ts\";\nimport { CloudflareDurableRuntimeConfig } from \"./CloudflareConfig.ts\";\nimport { safeCauseMessage } from \"./internal/boundary.ts\";\n\n/**\n * The single multiplexed Durable Object alarm (decision D-P6-2). A Durable Object has ONE\n * alarm slot; every cadence the Node host ran on fibers (wake scan, lease expiry, settlement\n * and abort re-checks, retry backoff) multiplexes into one idempotent maintenance pass, and\n * the slot always holds the EARLIEST deadline any caller asked for.\n *\n * The alarm invariant (plan §1.4): every committed actionable mutation carries a newer durable\n * maintenance generation and a committed alarm. Stable externally-driven waits may be\n * nonterminal without retaining an alarm; their resolving mutation advances the generation and\n * restores the alarm atomically.\n */\n\n/** The Durable Object alarm API failed; surfaces on host entry points as a typed refusal. */\nexport class DurableAlarmError extends Schema.TaggedError<DurableAlarmError>()(\n \"DurableAlarmError\",\n {\n operation: Schema.String,\n message: Schema.String,\n cause: Schema.optionalKey(Schema.Defect()),\n },\n) {}\n\nconst alarmFailure =\n (operation: string) =>\n (cause: unknown): DurableAlarmError =>\n DurableAlarmError.make({\n operation,\n message: safeCauseMessage(cause, \"The Cloudflare alarm API failed without a diagnostic\"),\n cause,\n });\n\n// SQL and raw KV/alarm operations share one physical SQLite transaction. Reserve its\n// connection for each short storage operation, never around a mutation or snapshot body.\nconst makeStorageOperation = Effect.map(\n SqlClient,\n (sql) =>\n <A>(operation: string, execute: () => Promise<A>) =>\n Effect.flatMap(Effect.serviceOption(sql.transactionService), (current) => {\n const body = Effect.uninterruptible(\n Effect.tryPromise({ try: execute, catch: alarmFailure(operation) }),\n );\n\n return current._tag === \"Some\"\n ? body\n : Effect.scoped(\n Effect.andThen(sql.reserve.pipe(Effect.mapError(alarmFailure(operation))), body),\n );\n }),\n);\n\n/** `ctx.storage` alarm slot as an Effect service; storage is truth, never a memory field. */\nexport class DurableAlarmService extends Context.Service<\n DurableAlarmService,\n {\n /** The scheduled deadline in epoch milliseconds, if any. */\n readonly scheduled: Effect.Effect<Option.Option<number>, DurableAlarmError>;\n /** Replace the slot with this deadline. */\n readonly scheduleAt: (epochMillis: number) => Effect.Effect<void, DurableAlarmError>;\n /** Keep the EARLIER of the existing deadline and this one (the multiplexing rule). */\n readonly ensureScheduledBy: (epochMillis: number) => Effect.Effect<void, DurableAlarmError>;\n /**\n * Arm an immediate alarm (the durable, coalescing local wake) — DEFERRED while a\n * maintenance pass is executing. Workerd cancels an in-flight alarm handler when a new\n * EARLIER deadline is written during its execution (`requestScheduledAlarm`), and the\n * maintenance pass runs INSIDE the alarm handler: an immediate wake landing mid-pass\n * (a routed port mutation, a sibling's `wake()`, the coordinator's own local notify)\n * would kill the running Attempt — manufacturing an ownership loss no real eviction\n * caused, and routing open uncertain-class Tool Calls into spurious Unknown Outcomes.\n * Deferral is contract-safe: wakes are droppable hints, every mutating entry point\n * pre-arms BEFORE its first durable mutation (the alarm invariant never rests on this\n * call). The pass's durable generation check observes any racing mutation, so the\n * in-memory hint does not need to be flushed after a stable wait is acknowledged.\n */\n readonly scheduleNow: Effect.Effect<void, DurableAlarmError>;\n /**\n * Run one maintenance pass with wake deferral (see `scheduleNow`). Calls made while `body`\n * executes are droppable promptness hints; correctness rests on the durable generation.\n */\n readonly withWakesDeferred: <A, E, R>(body: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;\n /** Clear the slot; correctness-sensitive clears live in maintenance generation transactions. */\n readonly cancel: Effect.Effect<void, DurableAlarmError>;\n }\n>()(\"@effect-agent/platform-cloudflare/DurableAlarmService\") {\n static readonly layer: Layer.Layer<DurableAlarmService, never, DurableObjectContext | SqlClient> =\n Layer.effect(DurableAlarmService)(\n Effect.gen(function* () {\n const { ctx } = yield* DurableObjectContext;\n /**\n * In-memory pass bookkeeping — a pure CACHE, never state: a fresh incarnation has no\n * running pass, and a deferred wake lost to eviction was only ever a promptness hint\n * on top of the already-committed pre-armed alarm.\n */\n const runningPasses = yield* Ref.make(0);\n\n const storageOperation = yield* makeStorageOperation;\n\n const scheduled = storageOperation(\"get alarm\", () => ctx.storage.getAlarm()).pipe(\n Effect.map((deadline) =>\n deadline === null ? Option.none<number>() : Option.some(deadline),\n ),\n );\n\n const scheduleAt = (epochMillis: number) =>\n storageOperation(\"set alarm\", () => ctx.storage.setAlarm(epochMillis));\n\n const ensureScheduledBy = (epochMillis: number) =>\n storageOperation(\"ensure alarm\", () =>\n ctx.storage.transaction(async (transaction) => {\n const existing = await transaction.getAlarm();\n\n if (existing === null || existing > epochMillis) {\n await transaction.setAlarm(epochMillis);\n }\n }),\n );\n\n const armNow = Clock.currentTimeMillis.pipe(\n Effect.flatMap((now) => ensureScheduledBy(now)),\n );\n\n const scheduleNow = Ref.get(runningPasses).pipe(\n Effect.flatMap((passes) => (passes > 0 ? Effect.void : armNow)),\n );\n\n const withWakesDeferred = <A, E, R>(body: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n Ref.update(runningPasses, (passes) => passes + 1).pipe(\n Effect.andThen(body),\n Effect.ensuring(Ref.update(runningPasses, (passes) => passes - 1)),\n );\n\n const cancel = storageOperation(\"delete alarm\", () => ctx.storage.deleteAlarm());\n\n return DurableAlarmService.of({\n scheduled,\n scheduleAt,\n ensureScheduledBy,\n scheduleNow,\n withWakesDeferred,\n cancel,\n });\n }),\n );\n}\n\n/** What one maintenance pass did — auditable evidence mirroring `NodeDurableHost`'s report. */\nexport class MaintenancePassReport extends Schema.Class<MaintenancePassReport>(\n \"@effect-agent/platform-cloudflare/MaintenancePassReport\",\n)({\n /** `caught-up` ran no runtime work (publication may be pending); `actionable` ran recovery. */\n phase: Schema.Literals([\"caught-up\", \"actionable\"]),\n /** Recovery decisions executed (or deferred) BEFORE any new claim in this pass. */\n recovered: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** Whether the head Attempt settled. Joined input may settle with that head. */\n settled: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** Submissions still nonterminal after the pass (suspended/unknown lanes stay honest). */\n nonterminal: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** `rearmed` for dirty/autonomous work, `cleared` for stable waits or settlement. */\n alarm: Schema.Literals([\"rearmed\", \"cleared\"]),\n}) {}\n\n/** Fault boundaries around every maintenance-owned durable mutation. */\nexport type ThreadMaintenanceFailpointLocation =\n | \"maintenance:dirty:before\"\n | \"maintenance:dirty:after\"\n | \"maintenance:mutation:armed\"\n | \"maintenance:mutation:finished\"\n | \"maintenance:ensure:before\"\n | \"maintenance:ensure:after\"\n | \"maintenance:begin:before\"\n | \"maintenance:begin:after\"\n | \"maintenance:select:before\"\n | \"maintenance:select:after\"\n | \"maintenance:finish:before\"\n | \"maintenance:finish:after\";\n\nexport type ThreadMaintenanceFailpointHandler = (\n location: ThreadMaintenanceFailpointLocation,\n) => Effect.Effect<void>;\n\n/** Test-only fault authority; production uses the inert layer. */\nexport class ThreadMaintenanceFailpoint extends Context.Service<\n ThreadMaintenanceFailpoint,\n {\n readonly hit: ThreadMaintenanceFailpointHandler;\n }\n>()(\"@effect-agent/platform-cloudflare/ThreadMaintenanceFailpoint\") {\n static readonly layer = Layer.succeed(this)({ hit: () => Effect.void });\n}\n\n/**\n * Durable host publication of canonical records and ledger approval/abort/resolution intents.\n * The host owns schema-versioned cursors, destination idempotency and acknowledgement. Delivery\n * is at least once. Hooks must not write the alarm slot or mutate the supplied raw source ports.\n *\n * `invalidate`, `prepareGeneration` and `pendingDeadline` must be bounded local operations.\n * `prepareGeneration` durably invalidates a scan only when its generation changes; repeated\n * calls must preserve partial scan progress. It runs with no source mutation in flight.\n * `drain` performs bounded delivery and persists retries before returning. A pending deadline\n * defers runtime recovery/Attempts, allowing committed host publications to drain first.\n * Unexpected hook failures leave the prearmed generation for retry. Hooks acquire per-call\n * resources with Effect.scoped; Layer construction owns incarnation resources (eviction need\n * not run finalizers). Do not hold a local hook behind network I/O or call back into producers.\n */\nexport interface ThreadPublicationService {\n readonly invalidate: Effect.Effect<void, DurableAlarmError>;\n readonly prepareGeneration: (generation: bigint) => Effect.Effect<void, DurableAlarmError>;\n readonly drain: Effect.Effect<void, DurableAlarmError>;\n readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;\n}\n\n/** Opt in with `ThreadObject.layer(registrations, { publication: Layer.effect(ThreadPublication)(...) })`. */\nexport class ThreadPublication extends Context.Service<\n ThreadPublication,\n ThreadPublicationService\n>()(\"@effect-agent/platform-cloudflare/ThreadPublication\") {\n static readonly layer = Layer.succeed(this)({\n invalidate: Effect.void,\n prepareGeneration: () => Effect.void,\n drain: Effect.void,\n pendingDeadline: Effect.succeed(Option.none()),\n });\n}\n\n/**\n * Host-assembled message recovery, supplied by ThreadObject.layer even when the application\n * rebuilds ThreadMaintenance. These obligations outlive source Runs and never defer a ready\n * source Attempt while a destination is processing an accepted message.\n */\nexport const ThreadMessageDelivery = Context.Reference<{\n readonly drain: Effect.Effect<void, DurableAlarmError>;\n /** Drain inserts and due retries during source work, finishing the bounded wave on completion. */\n readonly drainUntil?: (\n finished: Deferred.Deferred<void>,\n ) => Effect.Effect<void, DurableAlarmError>;\n readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;\n}>(\"@effect-agent/platform-cloudflare/ThreadMessageDelivery\", {\n defaultValue: () => ({ drain: Effect.void, pendingDeadline: Effect.succeed(Option.none()) }),\n});\n\n/**\n * Application obligations sharing this Object's alarm. The deadline read is local and\n * read-only. Drain beside the native Attempt and always finish one initial bounded wave,\n * even when `finished` was already signalled. Then stop starting new waves on that signal\n * and finish the bounded current wave before returning. Native maintenance joins that work\n * before acknowledging a generation. Mutations use the same ThreadMutationGate; hooks never\n * write the raw alarm slot. Pending host work does not defer a ready model Attempt.\n */\nexport const ThreadHostMaintenance = Context.Reference<{\n readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;\n readonly drainUntil: (\n finished: Deferred.Deferred<void>,\n ) => Effect.Effect<void, DurableAlarmError>;\n}>(\"@effect-agent/platform-cloudflare/ThreadHostMaintenance\", {\n defaultValue: () => ({\n pendingDeadline: Effect.succeed(Option.none()),\n drainUntil: () => Effect.void,\n }),\n});\n\nconst earliestDeadline = (\n left: Option.Option<number>,\n right: Option.Option<number>,\n): Option.Option<number> =>\n Option.isSome(left)\n ? Option.isSome(right)\n ? Option.some(Math.min(left.value, right.value))\n : left\n : right;\n\n/** @internal A committed source operation must not become a failed operation because delivery failed. */\nexport const publishCommitted = Effect.gen(function* () {\n const publication = yield* ThreadPublication;\n\n yield* publication.invalidate.pipe(Effect.andThen(publication.drain));\n}).pipe(\n Effect.catchCause((cause) =>\n Cause.hasInterrupts(cause)\n ? Effect.interrupt\n : Effect.logError(\"Thread publication deferred after source commit\", cause),\n ),\n);\n\nconst MaintenanceGeneration = Schema.BigIntFromString.check(\n Schema.isGreaterThanOrEqualToBigInt(0n),\n);\n\n/** Versioned, platform-private maintenance state stored through Durable Object KV. */\nclass ThreadMaintenanceState extends Schema.Class<ThreadMaintenanceState>(\n \"@effect-agent/platform-cloudflare/ThreadMaintenanceState\",\n)({\n schemaVersion: Schema.Literal(1),\n dirty: MaintenanceGeneration,\n processed: MaintenanceGeneration,\n nonterminal: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** One physical-owner cursor; old single-lane records need no conversion. */\n lastServedThreadId: Schema.optionalKey(ThreadId),\n}) {}\n\nconst MAINTENANCE_STATE_KEY = \"effect-agent:thread-maintenance:v1\";\nconst decodeMaintenanceState = Schema.decodeUnknownSync(ThreadMaintenanceState);\nconst encodeMaintenanceState = Schema.encodeSync(ThreadMaintenanceState);\n\nconst initialMaintenanceState = (): ThreadMaintenanceState =>\n ThreadMaintenanceState.make({\n schemaVersion: 1,\n // Bootstrap Objects created by the pre-generation release without scanning the ledger in\n // the constructor. One useful pass classifies and acknowledges any existing obligation.\n dirty: 1n,\n processed: 0n,\n nonterminal: 0,\n });\n\nconst readMaintenanceState = async (\n transaction: DurableObjectTransaction,\n): Promise<{ readonly state: ThreadMaintenanceState; readonly initialized: boolean }> => {\n const encoded = await transaction.get(MAINTENANCE_STATE_KEY);\n\n return encoded === undefined\n ? { state: initialMaintenanceState(), initialized: false }\n : { state: decodeMaintenanceState(encoded), initialized: true };\n};\n\nconst ensureTransactionAlarmBy = async (\n transaction: DurableObjectTransaction,\n deadline: number,\n): Promise<void> => {\n const scheduled = await transaction.getAlarm();\n\n if (scheduled === null || scheduled > deadline) {\n await transaction.setAlarm(deadline);\n }\n};\n\nconst stableExternalWait = (\n snapshot: SubmissionSnapshot,\n reports: ReadonlyMap<string, RecoveryReport>,\n): boolean => {\n const decision = reports.get(snapshot.submissionId)?.decision._tag;\n\n // An accepted abort still owes cleanup/settlement even if its claim was deferred this pass.\n if (decision === \"SettleAborted\") return false;\n switch (snapshot.state) {\n case \"suspended\":\n case \"joined\":\n return true;\n case \"unknown\":\n return decision === \"AwaitUnknownResolution\" || decision === \"MarkUnknown\";\n case \"admitted\":\n return reports.get(snapshot.submissionId)?.decision._tag === \"AwaitParentEstablishment\";\n case \"input-applied\":\n case \"joining\":\n case \"ready\":\n case \"running\":\n case \"settled\":\n case \"terminalizing\":\n return false;\n }\n};\n\n/**\n * Shared prearm/acknowledgement boundary for ingress and runtime-owned producers.\n * `ThreadObject.layer` provides this same instance in its Services. Rebuilt runtime/maintenance\n * Layers must reuse that instance; a second gate cannot observe the native producers' activity.\n */\nexport class ThreadMutationGate extends Context.Service<\n ThreadMutationGate,\n {\n readonly withMutation: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n /** Indexed host work has its own durable deadline and does not invalidate ledger recovery. */\n options?: { readonly invalidatesRecovery: boolean },\n ) => Effect.Effect<A, E | DurableAlarmError, R>;\n readonly withSnapshot: <A, E, R>(\n body: (active: number) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E, R>;\n }\n>()(\"@effect-agent/platform-cloudflare/internal/ThreadMutationGate\") {\n static readonly layer = Layer.effect(this)(\n Effect.gen(function* () {\n const { ctx } = yield* DurableObjectContext;\n const config = yield* CloudflareDurableRuntimeConfig;\n const failpoint = yield* ThreadMaintenanceFailpoint;\n // A fresh incarnation has no live mutations; durable generations survive eviction.\n const activeMutations = yield* Ref.make(0);\n const generationGate = yield* Semaphore.make(1);\n const minimumAlarmDelay = Math.max(1, Math.ceil(config.alarmBackoffBase / 2));\n\n const runTransaction = yield* makeStorageOperation;\n\n const beginMutation = Effect.fn(\"ThreadMaintenance.beginMutation\")(function* (\n invalidatesRecovery: boolean,\n ) {\n yield* failpoint.hit(\"maintenance:dirty:before\");\n const now = yield* Clock.currentTimeMillis;\n\n yield* runTransaction(\"advance maintenance generation\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state, initialized } = await readMaintenanceState(transaction);\n\n const next = ThreadMaintenanceState.make({\n ...state,\n dirty: state.dirty + (invalidatesRecovery ? 1n : 0n),\n });\n\n if (invalidatesRecovery || !initialized)\n await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(next));\n // The earliest configured retry bounds a newly actionable mutation without relying\n // on its best-effort immediate wake hint.\n await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);\n }),\n );\n yield* failpoint.hit(\"maintenance:dirty:after\");\n yield* Ref.update(activeMutations, (active) => active + 1);\n });\n\n const endMutation = generationGate.withPermit(\n Ref.update(activeMutations, (active) => Math.max(0, active - 1)),\n );\n\n const withMutation = <A, E, R>(\n body: Effect.Effect<A, E, R>,\n options?: { readonly invalidatesRecovery: boolean },\n ): Effect.Effect<A, E | DurableAlarmError, R> =>\n Effect.acquireUseRelease(\n generationGate.withPermit(beginMutation(options?.invalidatesRecovery ?? true)),\n () =>\n failpoint.hit(\"maintenance:mutation:armed\").pipe(\n Effect.andThen(body),\n Effect.tap(() => failpoint.hit(\"maintenance:mutation:finished\")),\n ),\n () => endMutation,\n );\n\n return ThreadMutationGate.of({\n withMutation,\n withSnapshot: (body) =>\n generationGate.withPermit(Effect.flatMap(Ref.get(activeMutations), body)),\n });\n }),\n );\n}\n\nexport type MaintenancePassFailure =\n | DurableWorkerFailure\n | DurableBindingFailure\n | DurableAlarmError\n | ThreadProjectionError;\n\n/**\n * Incremental, quiescent maintenance over a durable dirty/processed generation (issue #93).\n *\n * `pass` = generation snapshot/pre-arm → recovery → one head Attempt → generation acknowledgement:\n *\n * 1. One storage transaction reads dirty/processed and re-arms before work. A caught-up forced\n * alarm takes an O(1) path without recovery, ledger scans, or canonical-history reads.\n * 2. Recovery strictly precedes a new claim. One head Attempt advances the lane and requests\n * a safe yield after ten minutes. The whole event has a fourteen-minute cooperative timeout.\n * 3. The final transaction acknowledges only the generation observed at pass start. A racing\n * mutation therefore remains `dirty > processed` and retains its atomically-established alarm.\n * 4. Stable external waits acknowledge and clear. Autonomous retry, indeterminate, and lease\n * recovery states leave their generation dirty and retain bounded backoff rearming.\n */\nexport class ThreadMaintenance extends Context.Service<\n ThreadMaintenance,\n {\n /** One idempotent maintenance pass; failures propagate so workerd retries the alarm. */\n readonly pass: Effect.Effect<MaintenancePassReport, MaintenancePassFailure>;\n /**\n * Constructor gate: initialize/inspect only the O(1) maintenance record and ensure a dirty\n * generation has an alarm. It never scans the ledger or canonical history.\n */\n readonly ensureAlarm: Effect.Effect<void, MaintenancePassFailure>;\n /**\n * Serialize the pre-arm boundary with pass acknowledgement, advance the durable dirty\n * generation and arm the alarm in one transaction BEFORE running the caller's mutation.\n * A pass cannot acknowledge while that mutation remains in flight.\n */\n readonly withMutation: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | DurableAlarmError, R>;\n }\n>()(\"@effect-agent/platform-cloudflare/ThreadMaintenance\") {\n static readonly layer: Layer.Layer<\n ThreadMaintenance,\n never,\n | ThreadMutationGate\n | ThreadPublication\n | ThreadProjectionMaintenance\n | DurableAgentRuntime\n | SubmissionLedger\n | DurableAlarmService\n | ThreadMaintenanceFailpoint\n | CloudflareDurableRuntimeConfig\n | DurableObjectContext\n | SqlClient\n > = Layer.effect(ThreadMaintenance)(\n Effect.gen(function* () {\n const runtime = yield* DurableAgentRuntime;\n const ledger = yield* SubmissionLedger;\n const alarm = yield* DurableAlarmService;\n const config = yield* CloudflareDurableRuntimeConfig;\n const { ctx } = yield* DurableObjectContext;\n const failpoint = yield* ThreadMaintenanceFailpoint;\n\n /**\n * Consecutive no-progress passes — an in-memory CACHE, not state: a fresh incarnation\n * restarts at zero and merely re-arms sooner than a long-lived one would have.\n */\n const stalls = yield* Ref.make(0);\n const mutations = yield* ThreadMutationGate;\n const publication = yield* ThreadPublication;\n const projection = yield* ThreadProjectionMaintenance;\n const messages = yield* ThreadMessageDelivery;\n const host = yield* ThreadHostMaintenance;\n\n // A broken disposable index still needs a retry alarm and must not prevent startup.\n const projectionDeadline = projection.pendingDeadline.pipe(\n Effect.catchCauseIf(\n (cause) => !Cause.hasInterrupts(cause),\n (cause) =>\n Effect.logError(\"Thread projection deadline unavailable\", cause).pipe(\n Effect.as(Option.some(0)),\n ),\n ),\n );\n\n const pendingDeadline = Effect.gen(function* () {\n return earliestDeadline(\n earliestDeadline(yield* publication.pendingDeadline, yield* messages.pendingDeadline),\n earliestDeadline(yield* projectionDeadline, yield* host.pendingDeadline),\n );\n });\n\n const maintenancePassGate = yield* Semaphore.make(1);\n const minimumAlarmDelay = Math.max(1, Math.ceil(config.alarmBackoffBase / 2));\n\n const runTransaction = yield* makeStorageOperation;\n\n const ensureAlarm = Effect.fn(\"ThreadMaintenance.ensureAlarm\")(function* () {\n yield* failpoint.hit(\"maintenance:ensure:before\");\n const now = yield* Clock.currentTimeMillis;\n\n yield* runTransaction(\"ensure maintenance alarm\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state, initialized } = await readMaintenanceState(transaction);\n\n if (!initialized) {\n await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(state));\n }\n if (state.dirty > state.processed) {\n await ensureTransactionAlarmBy(transaction, now + config.wakeScanInterval);\n }\n }),\n );\n const deadline = yield* pendingDeadline;\n\n if (Option.isSome(deadline)) {\n yield* runTransaction(\"ensure publication alarm\", () =>\n ctx.storage.transaction((transaction) =>\n ensureTransactionAlarmBy(\n transaction,\n Math.max(now + minimumAlarmDelay, deadline.value),\n ),\n ),\n );\n }\n yield* failpoint.hit(\"maintenance:ensure:after\");\n });\n\n const beginPass = Effect.fn(\"ThreadMaintenance.beginPass\")(function* () {\n yield* failpoint.hit(\"maintenance:begin:before\");\n const now = yield* Clock.currentTimeMillis;\n\n const result = yield* runTransaction(\"begin maintenance pass\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state, initialized } = await readMaintenanceState(transaction);\n\n if (!initialized) {\n await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(state));\n }\n if (state.processed >= state.dirty) {\n // Prearm even a publication-only pass before invoking any host hook.\n await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);\n\n return { _tag: \"CaughtUp\" as const, nonterminal: state.nonterminal };\n }\n // Pre-arm the earliest retry before recovery. A successful finish may move this slot\n // LATER to its bounded backoff, which does not cancel the running handler.\n await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);\n\n return {\n _tag: \"Actionable\" as const,\n generation: state.dirty,\n nonterminal: state.nonterminal,\n };\n }),\n );\n\n yield* failpoint.hit(\"maintenance:begin:after\");\n\n return result;\n });\n\n const rearmDelay = Effect.fn(\"ThreadMaintenance.rearmDelay\")(function* (progressed: boolean) {\n const priorStalls = yield* Ref.getAndUpdate(stalls, (count) =>\n progressed ? 0 : count + 1,\n );\n\n if (progressed) return config.alarmBackoffBase;\n const exponent = Math.min(priorStalls, 30);\n const backoff = Math.min(config.alarmBackoffCap, config.alarmBackoffBase * 2 ** exponent);\n const jitter = yield* Random.next;\n // Full jitter over [backoff/2, backoff]: desynchronizes retry storms without ever\n // waiting longer than the deterministic bound.\n const jittered = Math.ceil(backoff / 2 + (backoff / 2) * jitter);\n\n return Math.min(jittered, config.wakeScanInterval);\n });\n\n const pass = Effect.fn(\"ThreadMaintenance.pass\")(function* (\n yieldAfter: DateTime.Utc,\n ): Effect.fn.Return<MaintenancePassReport, MaintenancePassFailure> {\n const annotate = (report: MaintenancePassReport) =>\n Effect.annotateCurrentSpan({\n phase: report.phase,\n recovered: report.recovered,\n settled: report.settled,\n nonterminal: report.nonterminal,\n alarm: report.alarm,\n }).pipe(Effect.as(report));\n\n const started = yield* mutations.withSnapshot((activeAtStart) =>\n Effect.gen(function* () {\n const generation = yield* beginPass();\n\n if (generation._tag === \"Actionable\" && activeAtStart === 0) {\n // The gate excludes a producer starting between the snapshot and certification.\n yield* publication.prepareGeneration(generation.generation);\n }\n\n return { ...generation, activeAtStart };\n }),\n );\n\n // Deliver beside source work, including messages inserted by the running Attempt.\n // Slow destination RPCs never consume the source execution window. Stop starting\n // waves when source work ends, then join the bounded current wave before acknowledgement.\n const deliveryFinished = yield* Deferred.make<void>();\n\n const delivery = yield* Effect.forkChild(\n messages.drainUntil?.(deliveryFinished) ?? messages.drain,\n );\n\n const hostWork = yield* Effect.forkChild(host.drainUntil(deliveryFinished));\n\n const finishDelivery = Deferred.succeed(deliveryFinished, undefined).pipe(\n Effect.andThen(Fiber.awaitAll([delivery, hostWork])),\n Effect.flatMap((outcomes) =>\n Effect.forEach(outcomes, (outcome) => outcome, { discard: true }),\n ),\n );\n\n // Capture derived-index failures until canonical work has had its turn. Interruption\n // still stops the event; ordinary failures and defects retain the prearmed generation.\n const projected = yield* Effect.exit(\n drainDue.pipe(Effect.provideService(ThreadProjectionMaintenance, projection)),\n );\n\n if (Exit.isFailure(projected) && Cause.hasInterrupts(projected.cause))\n return yield* Effect.failCause(projected.cause);\n const deadline = yield* publication.pendingDeadline;\n\n if (\n started._tag === \"Actionable\" ||\n (Option.isSome(deadline) && deadline.value <= (yield* Clock.currentTimeMillis))\n ) {\n yield* publication.drain;\n }\n const pending = yield* publication.pendingDeadline;\n\n if (started._tag === \"CaughtUp\" || Option.isSome(pending)) {\n yield* finishDelivery;\n if (Exit.isFailure(projected)) return yield* Effect.failCause(projected.cause);\n yield* failpoint.hit(\"maintenance:finish:before\");\n\n const disposition = yield* mutations.withSnapshot((active) =>\n Effect.gen(function* () {\n // Re-read under the producer gate: a concurrent append/host mutation cannot be\n // cleared using a stale empty deadline. Dirty generations bound all producer races.\n const latest = yield* pendingDeadline;\n const now = yield* Clock.currentTimeMillis;\n\n return yield* runTransaction(\"finish publication pass\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state } = await readMaintenanceState(transaction);\n\n const nativeDeadline =\n active > 0 || state.dirty > state.processed\n ? now + config.wakeScanInterval\n : Infinity;\n\n const next = Option.isSome(latest)\n ? Math.min(nativeDeadline, latest.value)\n : nativeDeadline;\n\n if (Number.isFinite(next)) {\n await transaction.setAlarm(Math.max(now + minimumAlarmDelay, next));\n\n return \"rearmed\" as const;\n }\n await transaction.deleteAlarm();\n\n return \"cleared\" as const;\n }),\n );\n }),\n );\n\n yield* failpoint.hit(\"maintenance:finish:after\");\n\n return yield* annotate(\n MaintenancePassReport.make({\n phase: \"caught-up\",\n recovered: 0,\n settled: 0,\n nonterminal: started.nonterminal,\n alarm: disposition,\n }),\n );\n }\n // Step 2 — reconciliation strictly precedes new work in this pass (exit gate).\n const recovered: ReadonlyArray<RecoveryReport> = yield* runtime.runRecovery;\n const reports = new Map(recovered.map((report) => [report.submissionId, report]));\n const current = yield* Stream.runCollect(ledger.scanNonterminal);\n const heads = new Map<ThreadId, SubmissionSnapshot>();\n\n for (const row of current) {\n if (!heads.has(row.threadId)) heads.set(row.threadId, row);\n }\n\n const eligible = [...heads.values()]\n .filter((head) => !stableExternalWait(head, reports))\n .map((head) => head.threadId)\n .sort();\n\n let selected = eligible[0];\n\n if (heads.size > 1 && selected !== undefined) {\n yield* failpoint.hit(\"maintenance:select:before\");\n selected = yield* runTransaction(\"select maintenance lane\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state } = await readMaintenanceState(transaction);\n\n const next =\n eligible.find(\n (threadId) =>\n state.lastServedThreadId === undefined || threadId > state.lastServedThreadId,\n ) ?? eligible[0];\n\n if (next !== undefined) {\n // Persist before the Attempt so an eviction or repeated yield cannot\n // monopolize the first lane. The generation and prearmed alarm survive.\n await transaction.put(\n MAINTENANCE_STATE_KEY,\n encodeMaintenanceState(\n ThreadMaintenanceState.make({ ...state, lastServedThreadId: next }),\n ),\n );\n }\n\n return next;\n }),\n );\n yield* failpoint.hit(\"maintenance:select:after\");\n }\n\n // One FIFO head per event, across all local lanes. The runtime keeps its normal\n // bounded Attempt and recovery contracts; followers belong to another alarm.\n const settlement =\n selected === undefined\n ? Option.none()\n : yield* runtime.processThreadHead(selected, { yieldAfter });\n\n yield* finishDelivery;\n if (Exit.isFailure(projected)) return yield* Effect.failCause(projected.cause);\n // Observe residual state before acknowledging this exact pass-start generation.\n const remaining = yield* Stream.runCollect(ledger.scanNonterminal);\n const waitingHeads = new Map<ThreadId, boolean>();\n\n const autonomous = remaining.some((snapshot) => {\n const headWaiting = waitingHeads.get(snapshot.threadId);\n\n if (headWaiting === undefined)\n waitingHeads.set(snapshot.threadId, stableExternalWait(snapshot, reports));\n // FIFO followers cannot execute through a stable external wait. Only plain queued\n // input is dormant here; admission repairs and accepted aborts still need a pass.\n if (\n headWaiting === true &&\n snapshot.state === \"ready\" &&\n reports.get(snapshot.submissionId)?.decision._tag === \"ApplyInput\"\n )\n return false;\n\n return !stableExternalWait(snapshot, reports);\n });\n\n const progressed =\n Option.isSome(settlement) ||\n recovered.some((report) => report.disposition === \"repaired\");\n\n const delay = autonomous ? yield* rearmDelay(progressed) : 0;\n const now = yield* Clock.currentTimeMillis;\n\n yield* failpoint.hit(\"maintenance:finish:before\");\n\n const alarmDisposition = yield* mutations.withSnapshot((active) =>\n Effect.gen(function* () {\n const publicationDeadline = yield* pendingDeadline;\n\n return yield* runTransaction(\"finish maintenance pass\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state } = await readMaintenanceState(transaction);\n\n // Autonomous work and in-flight mutations intentionally leave the observed\n // generation dirty. Otherwise acknowledge only the pass-start generation.\n const processed =\n autonomous || started.activeAtStart > 0 || active > 0\n ? state.processed\n : state.processed > started.generation\n ? state.processed\n : started.generation;\n\n const next = ThreadMaintenanceState.make({\n ...state,\n processed,\n nonterminal: remaining.length,\n });\n\n await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(next));\n if (autonomous) {\n // Replace the crash-fallback slot with this pass's bounded backoff. The target\n // is never earlier than the begin-pass fallback, so workerd does not cancel\n // this running alarm handler before its report/span can complete.\n await transaction.setAlarm(\n Option.isSome(publicationDeadline)\n ? Math.max(\n now + minimumAlarmDelay,\n Math.min(now + delay, publicationDeadline.value),\n )\n : now + delay,\n );\n\n return \"rearmed\" as const;\n }\n if (started.activeAtStart > 0 || active > 0 || next.dirty > next.processed) {\n // A mutation overlapped this pass's observation window or raced\n // acknowledgement. It stays dirty and its pre-armed bounded alarm survives;\n // unseen effects are never acknowledged. Do not accelerate that future alarm\n // from inside the current handler: workerd cancels a running handler when it\n // writes an earlier slot.\n await ensureTransactionAlarmBy(\n transaction,\n Option.isSome(publicationDeadline)\n ? Math.max(\n now + minimumAlarmDelay,\n Math.min(now + config.wakeScanInterval, publicationDeadline.value),\n )\n : now + config.wakeScanInterval,\n );\n\n return \"rearmed\" as const;\n }\n if (Option.isSome(publicationDeadline)) {\n await transaction.setAlarm(\n Math.max(now + minimumAlarmDelay, publicationDeadline.value),\n );\n\n return \"rearmed\" as const;\n }\n await transaction.deleteAlarm();\n\n return \"cleared\" as const;\n }),\n );\n }),\n );\n\n yield* failpoint.hit(\"maintenance:finish:after\");\n if (alarmDisposition === \"cleared\") {\n yield* Ref.set(stalls, 0);\n }\n\n return yield* annotate(\n MaintenancePassReport.make({\n phase: \"actionable\",\n recovered: recovered.length,\n settled: Option.isSome(settlement) ? 1 : 0,\n nonterminal: remaining.length,\n alarm: alarmDisposition,\n }),\n );\n });\n\n return ThreadMaintenance.of({\n // A mid-pass immediate hint is droppable; durable dirty state decides the final alarm.\n pass: Effect.gen(function* () {\n const yieldAfter = DateTime.makeUnsafe((yield* Clock.currentTimeMillis) + 10 * 60_000);\n\n return yield* alarm.withWakesDeferred(maintenancePassGate.withPermit(pass(yieldAfter)));\n }).pipe(\n // Include permit waiting, recovery and acknowledgement in the event deadline.\n // Interruption releases Attempt ownership, leaving the prearmed dirty generation\n // for recovery. It never changes the logical Run duration or settles a policy failure.\n // This cooperative timer cannot preempt synchronous CPU work or stuck finalizers.\n Effect.timeoutOrElse({\n duration: \"14 minutes\",\n orElse: () =>\n DurableAlarmError.make({\n operation: \"maintenance pass deadline\",\n message:\n \"The maintenance event exceeded its 14 minute deadline; durable recovery remains pending\",\n }),\n }),\n ),\n ensureAlarm: mutations.withSnapshot(() => ensureAlarm()),\n withMutation: (body) =>\n mutations.withMutation(\n body.pipe(\n Effect.tap(() =>\n publishCommitted.pipe(Effect.provideService(ThreadPublication, publication)),\n ),\n ),\n ),\n });\n }),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Alarm.mjs","names":[],"sources":["../src/Alarm.ts"],"sourcesContent":["import {\n Cause,\n Clock,\n Context,\n DateTime,\n Deferred,\n Effect,\n Exit,\n Fiber,\n Layer,\n Option,\n Random,\n Ref,\n Schema,\n Scope,\n Semaphore,\n Stream,\n} from \"effect\";\nimport { type DurableBindingFailure } from \"effect-agent/agent-registration\";\nimport {\n DurableAgentRuntime,\n type DurableWorkerFailure,\n type RecoveryReport,\n} from \"effect-agent/durable-agent-runtime\";\nimport { ThreadId } from \"effect-agent/identifiers\";\nimport { SubmissionLedger, type SubmissionSnapshot } from \"effect-agent/submission-ledger\";\nimport {\n ThreadProjectionMaintenance,\n drainDue,\n type ThreadProjectionError,\n} from \"effect-agent/thread-projection-maintenance\";\nimport { SqlClient } from \"effect/unstable/sql/SqlClient\";\n\nimport { DurableObjectContext } from \"./CloudflareBindings.ts\";\nimport { AuxiliaryDispatchMillis, CloudflareDurableRuntimeConfig } from \"./CloudflareConfig.ts\";\nimport { safeCauseMessage } from \"./internal/boundary.ts\";\n\n/**\n * The single multiplexed Durable Object alarm (decision D-P6-2). A Durable Object has ONE\n * alarm slot; every cadence the Node host ran on fibers (wake scan, lease expiry, settlement\n * and abort re-checks, retry backoff) multiplexes into one idempotent maintenance pass, and\n * the slot always holds the EARLIEST deadline any caller asked for.\n *\n * The alarm invariant (plan §1.4): every committed actionable mutation carries a newer durable\n * maintenance generation and a committed alarm. Stable externally-driven waits may be\n * nonterminal without retaining an alarm; their resolving mutation advances the generation and\n * restores the alarm atomically.\n */\n\n/** The Durable Object alarm API failed; surfaces on host entry points as a typed refusal. */\nexport class DurableAlarmError extends Schema.TaggedError<DurableAlarmError>()(\n \"DurableAlarmError\",\n {\n operation: Schema.String,\n message: Schema.String,\n cause: Schema.optionalKey(Schema.Defect()),\n },\n) {}\n\nconst alarmFailure =\n (operation: string) =>\n (cause: unknown): DurableAlarmError =>\n DurableAlarmError.make({\n operation,\n message: safeCauseMessage(cause, \"The Cloudflare alarm API failed without a diagnostic\"),\n cause,\n });\n\n// SQL and raw KV/alarm operations share one physical SQLite transaction. Reserve its\n// connection for each short storage operation, never around a mutation or snapshot body.\nconst makeStorageOperation = Effect.map(\n SqlClient,\n (sql) =>\n <A>(operation: string, execute: () => Promise<A>) =>\n Effect.flatMap(Effect.serviceOption(sql.transactionService), (current) => {\n const body = Effect.uninterruptible(\n Effect.tryPromise({ try: execute, catch: alarmFailure(operation) }),\n );\n\n return current._tag === \"Some\"\n ? body\n : Effect.scoped(\n Effect.andThen(sql.reserve.pipe(Effect.mapError(alarmFailure(operation))), body),\n );\n }),\n);\n\n/** `ctx.storage` alarm slot as an Effect service; storage is truth, never a memory field. */\nexport class DurableAlarmService extends Context.Service<\n DurableAlarmService,\n {\n /** The scheduled deadline in epoch milliseconds, if any. */\n readonly scheduled: Effect.Effect<Option.Option<number>, DurableAlarmError>;\n /** Replace the slot with this deadline. */\n readonly scheduleAt: (epochMillis: number) => Effect.Effect<void, DurableAlarmError>;\n /** Keep the EARLIER of the existing deadline and this one (the multiplexing rule). */\n readonly ensureScheduledBy: (epochMillis: number) => Effect.Effect<void, DurableAlarmError>;\n /**\n * Arm an immediate alarm (the durable, coalescing local wake) — DEFERRED while a\n * maintenance pass is executing. Workerd cancels an in-flight alarm handler when a new\n * EARLIER deadline is written during its execution (`requestScheduledAlarm`), and the\n * maintenance pass runs INSIDE the alarm handler: an immediate wake landing mid-pass\n * (a routed port mutation, a sibling's `wake()`, the coordinator's own local notify)\n * would kill the running Attempt — manufacturing an ownership loss no real eviction\n * caused, and routing open uncertain-class Tool Calls into spurious Unknown Outcomes.\n * Deferral is contract-safe: wakes are droppable hints, every mutating entry point\n * pre-arms BEFORE its first durable mutation (the alarm invariant never rests on this\n * call). The pass's durable generation check observes any racing mutation, so the\n * in-memory hint does not need to be flushed after a stable wait is acknowledged.\n */\n readonly scheduleNow: Effect.Effect<void, DurableAlarmError>;\n /**\n * Run one maintenance pass with wake deferral (see `scheduleNow`). Calls made while `body`\n * executes are droppable promptness hints; correctness rests on the durable generation.\n */\n readonly withWakesDeferred: <A, E, R>(body: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;\n /** Clear the slot; correctness-sensitive clears live in maintenance generation transactions. */\n readonly cancel: Effect.Effect<void, DurableAlarmError>;\n }\n>()(\"@effect-agent/platform-cloudflare/DurableAlarmService\") {\n static readonly layer: Layer.Layer<DurableAlarmService, never, DurableObjectContext | SqlClient> =\n Layer.effect(DurableAlarmService)(\n Effect.gen(function* () {\n const { ctx } = yield* DurableObjectContext;\n /**\n * In-memory pass bookkeeping — a pure CACHE, never state: a fresh incarnation has no\n * running pass, and a deferred wake lost to eviction was only ever a promptness hint\n * on top of the already-committed pre-armed alarm.\n */\n const runningPasses = yield* Ref.make(0);\n\n const storageOperation = yield* makeStorageOperation;\n\n const scheduled = storageOperation(\"get alarm\", () => ctx.storage.getAlarm()).pipe(\n Effect.map((deadline) =>\n deadline === null ? Option.none<number>() : Option.some(deadline),\n ),\n );\n\n const scheduleAt = (epochMillis: number) =>\n storageOperation(\"set alarm\", () => ctx.storage.setAlarm(epochMillis));\n\n const ensureScheduledBy = (epochMillis: number) =>\n storageOperation(\"ensure alarm\", () =>\n ctx.storage.transaction(async (transaction) => {\n const existing = await transaction.getAlarm();\n\n if (existing === null || existing > epochMillis) {\n await transaction.setAlarm(epochMillis);\n }\n }),\n );\n\n const armNow = Clock.currentTimeMillis.pipe(\n Effect.flatMap((now) => ensureScheduledBy(now)),\n );\n\n const scheduleNow = Ref.get(runningPasses).pipe(\n Effect.flatMap((passes) => (passes > 0 ? Effect.void : armNow)),\n );\n\n const withWakesDeferred = <A, E, R>(body: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>\n Ref.update(runningPasses, (passes) => passes + 1).pipe(\n Effect.andThen(body),\n Effect.ensuring(Ref.update(runningPasses, (passes) => passes - 1)),\n );\n\n const cancel = storageOperation(\"delete alarm\", () => ctx.storage.deleteAlarm());\n\n return DurableAlarmService.of({\n scheduled,\n scheduleAt,\n ensureScheduledBy,\n scheduleNow,\n withWakesDeferred,\n cancel,\n });\n }),\n );\n}\n\n/** What one maintenance pass did — auditable evidence mirroring `NodeDurableHost`'s report. */\nexport class MaintenancePassReport extends Schema.Class<MaintenancePassReport>(\n \"@effect-agent/platform-cloudflare/MaintenancePassReport\",\n)({\n /** `caught-up` ran no runtime work (publication may be pending); `actionable` ran recovery. */\n phase: Schema.Literals([\"caught-up\", \"actionable\"]),\n /** Recovery decisions executed (or deferred) BEFORE any new claim in this pass. */\n recovered: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** Whether the head Attempt settled. Joined input may settle with that head. */\n settled: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** Submissions still nonterminal after the pass (suspended/unknown lanes stay honest). */\n nonterminal: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** `rearmed` for dirty/autonomous work, `cleared` for stable waits or settlement. */\n alarm: Schema.Literals([\"rearmed\", \"cleared\"]),\n}) {}\n\n/** Fault boundaries around every maintenance-owned durable mutation. */\nexport type ThreadMaintenanceFailpointLocation =\n | \"maintenance:dirty:before\"\n | \"maintenance:dirty:after\"\n | \"maintenance:mutation:armed\"\n | \"maintenance:mutation:finished\"\n | \"maintenance:ensure:before\"\n | \"maintenance:ensure:after\"\n | \"maintenance:begin:before\"\n | \"maintenance:begin:after\"\n | \"maintenance:select:before\"\n | \"maintenance:select:after\"\n | \"maintenance:finish:before\"\n | \"maintenance:finish:after\";\n\nexport type ThreadMaintenanceFailpointHandler = (\n location: ThreadMaintenanceFailpointLocation,\n) => Effect.Effect<void>;\n\n/** Test-only fault authority; production uses the inert layer. */\nexport class ThreadMaintenanceFailpoint extends Context.Service<\n ThreadMaintenanceFailpoint,\n {\n readonly hit: ThreadMaintenanceFailpointHandler;\n }\n>()(\"@effect-agent/platform-cloudflare/ThreadMaintenanceFailpoint\") {\n static readonly layer = Layer.succeed(this)({ hit: () => Effect.void });\n}\n\n/**\n * Durable host publication of canonical records and ledger approval/abort/resolution intents.\n * The host owns schema-versioned cursors, destination idempotency and acknowledgement. Delivery\n * is at least once. Hooks must not write the alarm slot or mutate the supplied raw source ports.\n *\n * `invalidate`, `prepareGeneration` and `pendingDeadline` must be bounded local operations.\n * `prepareGeneration` durably invalidates a scan only when its generation changes; repeated\n * calls must preserve partial scan progress. It runs with no source mutation in flight.\n * Use this gate only for publication required before dependent native execution. Independent\n * UI relays and outboxes belong to ThreadHostMaintenance.\n * `drain` performs bounded delivery and persists retries before returning. A pending deadline\n * defers runtime recovery/Attempts, allowing committed host publications to drain first.\n * Unexpected hook failures leave the prearmed generation for retry. Hooks acquire per-call\n * resources with Effect.scoped; Layer construction owns incarnation resources (eviction need\n * not run finalizers). Do not hold a local hook behind network I/O or call back into producers.\n */\nexport interface ThreadPublicationService {\n readonly invalidate: Effect.Effect<void, DurableAlarmError>;\n readonly prepareGeneration: (generation: bigint) => Effect.Effect<void, DurableAlarmError>;\n readonly drain: Effect.Effect<void, DurableAlarmError>;\n readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;\n}\n\n/** Opt in with `ThreadObject.layer(registrations, { publication: Layer.effect(ThreadPublication)(...) })`. */\nexport class ThreadPublication extends Context.Service<\n ThreadPublication,\n ThreadPublicationService\n>()(\"@effect-agent/platform-cloudflare/ThreadPublication\") {\n static readonly layer = Layer.succeed(this)({\n invalidate: Effect.void,\n prepareGeneration: () => Effect.void,\n drain: Effect.void,\n pendingDeadline: Effect.succeed(Option.none()),\n });\n}\n\n/**\n * Host-assembled native message recovery. The driver bounds each actual Claim and persists its\n * timeout/retry before this pump returns. Do not add a second timer starting at batch selection:\n * local Claim setup may take time, and expiration still owes the driver's local retry commit.\n */\nexport const ThreadMessageDelivery = Context.Reference<{\n readonly drainUntil: (\n sourceFinished: Effect.Effect<void>,\n dispatchUntil: DateTime.Utc,\n ) => Effect.Effect<void, DurableAlarmError, Scope.Scope>;\n readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;\n}>(\"@effect-agent/platform-cloudflare/ThreadMessageDelivery\", {\n defaultValue: () => ({\n drainUntil: () => Effect.void,\n pendingDeadline: Effect.succeed(Option.none()),\n }),\n});\n\n/**\n * Application obligations sharing this Object's alarm. Admit one initial external wave even on\n * a caught-up pass, then respond to wakes while native work runs. sourceFinished closes only\n * admission of NEW external waves. Keep local admission/hub subscriptions in the supplied event\n * Scope until maintenance tears it down; do not scope them to sourceFinished or to this Effect.\n * No deadline sleeps or automatic retry loops. Return after already-admitted waves finish.\n *\n * Declare a finite whole-wave allowance (1..300000ms): maximum for parallel lanes, sum for\n * sequential operations. Admit a wave only if its allowance fits before dispatchUntil. Later\n * arrivals cannot renew the retirement window. Maintenance bounds the join after sourceFinished,\n * interrupts and joins event Scope, then reads local deadlines under the mutation gate.\n *\n * Setup and pendingDeadline are bounded local operations. Persist claims/envelopes before\n * dispatch; interrupted waits leave exact retries/receipts recoverable. Cancellation is local,\n * not remote rollback. Retry accounting is unchanged. Network waits/finalizers must be\n * interruptible; short local atomic commits may be uninterruptible. Hooks never write the raw\n * alarm slot. Required native publication gates belong to ThreadPublication.\n */\nexport const ThreadHostMaintenance = Context.Reference<{\n readonly dispatchTimeoutMillis: number;\n readonly drainUntil: (\n sourceFinished: Effect.Effect<void>,\n dispatchUntil: DateTime.Utc,\n ) => Effect.Effect<void, DurableAlarmError, Scope.Scope>;\n readonly pendingDeadline: Effect.Effect<Option.Option<number>, DurableAlarmError>;\n}>(\"@effect-agent/platform-cloudflare/ThreadHostMaintenance\", {\n defaultValue: () => ({\n dispatchTimeoutMillis: 1,\n drainUntil: () => Effect.void,\n pendingDeadline: Effect.succeed(Option.none()),\n }),\n});\n\nconst earliestDeadline = (\n left: Option.Option<number>,\n right: Option.Option<number>,\n): Option.Option<number> =>\n Option.isSome(left)\n ? Option.isSome(right)\n ? Option.some(Math.min(left.value, right.value))\n : left\n : right;\n\n/** @internal A committed source operation must not become a failed operation because delivery failed. */\nexport const publishCommitted = Effect.gen(function* () {\n const publication = yield* ThreadPublication;\n\n yield* publication.invalidate.pipe(Effect.andThen(publication.drain));\n}).pipe(\n Effect.catchCause((cause) =>\n Cause.hasInterrupts(cause)\n ? Effect.interrupt\n : Effect.logError(\"Thread publication deferred after source commit\", cause),\n ),\n);\n\nconst MaintenanceGeneration = Schema.BigIntFromString.check(\n Schema.isGreaterThanOrEqualToBigInt(0n),\n);\n\n/** Versioned, platform-private maintenance state stored through Durable Object KV. */\nclass ThreadMaintenanceState extends Schema.Class<ThreadMaintenanceState>(\n \"@effect-agent/platform-cloudflare/ThreadMaintenanceState\",\n)({\n schemaVersion: Schema.Literal(1),\n dirty: MaintenanceGeneration,\n processed: MaintenanceGeneration,\n nonterminal: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n /** One physical-owner cursor; old single-lane records need no conversion. */\n lastServedThreadId: Schema.optionalKey(ThreadId),\n}) {}\n\nconst MAINTENANCE_STATE_KEY = \"effect-agent:thread-maintenance:v1\";\nconst decodeMaintenanceState = Schema.decodeUnknownSync(ThreadMaintenanceState);\nconst encodeMaintenanceState = Schema.encodeSync(ThreadMaintenanceState);\n\nconst initialMaintenanceState = (): ThreadMaintenanceState =>\n ThreadMaintenanceState.make({\n schemaVersion: 1,\n // Bootstrap Objects created by the pre-generation release without scanning the ledger in\n // the constructor. One useful pass classifies and acknowledges any existing obligation.\n dirty: 1n,\n processed: 0n,\n nonterminal: 0,\n });\n\nconst readMaintenanceState = async (\n transaction: DurableObjectTransaction,\n): Promise<{ readonly state: ThreadMaintenanceState; readonly initialized: boolean }> => {\n const encoded = await transaction.get(MAINTENANCE_STATE_KEY);\n\n return encoded === undefined\n ? { state: initialMaintenanceState(), initialized: false }\n : { state: decodeMaintenanceState(encoded), initialized: true };\n};\n\nconst ensureTransactionAlarmBy = async (\n transaction: DurableObjectTransaction,\n deadline: number,\n): Promise<void> => {\n const scheduled = await transaction.getAlarm();\n\n if (scheduled === null || scheduled > deadline) {\n await transaction.setAlarm(deadline);\n }\n};\n\nconst stableExternalWait = (\n snapshot: SubmissionSnapshot,\n reports: ReadonlyMap<string, RecoveryReport>,\n): boolean => {\n const decision = reports.get(snapshot.submissionId)?.decision._tag;\n\n // An accepted abort still owes cleanup/settlement even if its claim was deferred this pass.\n if (decision === \"SettleAborted\") return false;\n switch (snapshot.state) {\n case \"suspended\":\n case \"joined\":\n return true;\n case \"unknown\":\n return decision === \"AwaitUnknownResolution\" || decision === \"MarkUnknown\";\n case \"admitted\":\n return reports.get(snapshot.submissionId)?.decision._tag === \"AwaitParentEstablishment\";\n case \"input-applied\":\n case \"joining\":\n case \"ready\":\n case \"running\":\n case \"settled\":\n case \"terminalizing\":\n return false;\n }\n};\n\n/**\n * Shared prearm/acknowledgement boundary for ingress and runtime-owned producers.\n * `ThreadObject.layer` provides this same instance in its Services. Rebuilt runtime/maintenance\n * Layers must reuse that instance; a second gate cannot observe the native producers' activity.\n */\nexport class ThreadMutationGate extends Context.Service<\n ThreadMutationGate,\n {\n readonly withMutation: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n /**\n * Native admission, approval, abort and unknown resolution keep the default true.\n * Projection/relay/reply receipt-only bookkeeping must use false: its local durable\n * pendingDeadline owns scheduling without creating native recovery debt.\n */\n options?: { readonly invalidatesRecovery: boolean },\n ) => Effect.Effect<A, E | DurableAlarmError, R>;\n readonly withSnapshot: <A, E, R>(\n body: (active: number) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E, R>;\n }\n>()(\"@effect-agent/platform-cloudflare/internal/ThreadMutationGate\") {\n static readonly layer = Layer.effect(this)(\n Effect.gen(function* () {\n const { ctx } = yield* DurableObjectContext;\n const config = yield* CloudflareDurableRuntimeConfig;\n const failpoint = yield* ThreadMaintenanceFailpoint;\n // A fresh incarnation has no live mutations; durable generations survive eviction.\n const activeMutations = yield* Ref.make(0);\n const generationGate = yield* Semaphore.make(1);\n const minimumAlarmDelay = Math.max(1, Math.ceil(config.alarmBackoffBase / 2));\n\n const runTransaction = yield* makeStorageOperation;\n\n const beginMutation = Effect.fn(\"ThreadMaintenance.beginMutation\")(function* (\n invalidatesRecovery: boolean,\n ) {\n yield* failpoint.hit(\"maintenance:dirty:before\");\n const now = yield* Clock.currentTimeMillis;\n\n yield* runTransaction(\"advance maintenance generation\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state, initialized } = await readMaintenanceState(transaction);\n\n const next = ThreadMaintenanceState.make({\n ...state,\n dirty: state.dirty + (invalidatesRecovery ? 1n : 0n),\n });\n\n if (invalidatesRecovery || !initialized)\n await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(next));\n // The earliest configured retry bounds a newly actionable mutation without relying\n // on its best-effort immediate wake hint.\n await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);\n }),\n );\n yield* failpoint.hit(\"maintenance:dirty:after\");\n yield* Ref.update(activeMutations, (active) => active + 1);\n });\n\n const endMutation = generationGate.withPermit(\n Ref.update(activeMutations, (active) => Math.max(0, active - 1)),\n );\n\n const withMutation = <A, E, R>(\n body: Effect.Effect<A, E, R>,\n options?: { readonly invalidatesRecovery: boolean },\n ): Effect.Effect<A, E | DurableAlarmError, R> =>\n Effect.acquireUseRelease(\n generationGate.withPermit(beginMutation(options?.invalidatesRecovery ?? true)),\n () =>\n failpoint.hit(\"maintenance:mutation:armed\").pipe(\n Effect.andThen(body),\n Effect.tap(() => failpoint.hit(\"maintenance:mutation:finished\")),\n ),\n () => endMutation,\n );\n\n return ThreadMutationGate.of({\n withMutation,\n withSnapshot: (body) =>\n generationGate.withPermit(Effect.flatMap(Ref.get(activeMutations), body)),\n });\n }),\n );\n}\n\nexport type MaintenancePassFailure =\n | DurableWorkerFailure\n | DurableBindingFailure\n | DurableAlarmError\n | ThreadProjectionError;\n\n/**\n * Incremental, quiescent maintenance over a durable dirty/processed generation (issue #93).\n *\n * `pass` = generation snapshot/pre-arm → recovery → one head Attempt → generation acknowledgement:\n *\n * 1. One storage transaction reads dirty/processed and re-arms before work. A caught-up forced\n * alarm takes an O(1) path without recovery, ledger scans, or canonical-history reads.\n * 2. Recovery strictly precedes a new claim. One head Attempt advances the lane and requests\n * a safe yield after ten minutes. The whole event has a fourteen-minute cooperative timeout.\n * 3. Auxiliary dispatch and listeners belong to the event Scope. After native completion,\n * stop new external waves, join native delivery through its driver-owned Claim deadline,\n * and bound host/backfill work by explicit allowances before closing and joining Scope.\n * Ordinary auxiliary setup failures are reported after the one native opportunity.\n * 4. The final transaction acknowledges only the generation observed at pass start. A racing\n * mutation therefore remains `dirty > processed` and retains its atomically-established alarm.\n * 5. Stable external waits acknowledge and clear. Autonomous retry, indeterminate, and lease\n * recovery states leave their generation dirty and retain bounded backoff rearming.\n */\nexport class ThreadMaintenance extends Context.Service<\n ThreadMaintenance,\n {\n /** One idempotent maintenance pass; failures propagate so workerd retries the alarm. */\n readonly pass: Effect.Effect<MaintenancePassReport, MaintenancePassFailure>;\n /**\n * Constructor gate: initialize/inspect only the O(1) maintenance record and ensure a dirty\n * generation has an alarm. It never scans the ledger or canonical history.\n */\n readonly ensureAlarm: Effect.Effect<void, MaintenancePassFailure>;\n /**\n * Serialize the pre-arm boundary with pass acknowledgement, advance the durable dirty\n * generation and arm the alarm in one transaction BEFORE running the caller's mutation.\n * A pass cannot acknowledge while that mutation remains in flight.\n */\n readonly withMutation: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | DurableAlarmError, R>;\n }\n>()(\"@effect-agent/platform-cloudflare/ThreadMaintenance\") {\n static readonly layer: Layer.Layer<\n ThreadMaintenance,\n never,\n | ThreadMutationGate\n | ThreadPublication\n | ThreadProjectionMaintenance\n | DurableAgentRuntime\n | SubmissionLedger\n | DurableAlarmService\n | ThreadMaintenanceFailpoint\n | CloudflareDurableRuntimeConfig\n | DurableObjectContext\n | SqlClient\n > = Layer.effect(ThreadMaintenance)(\n Effect.gen(function* () {\n const runtime = yield* DurableAgentRuntime;\n const ledger = yield* SubmissionLedger;\n const alarm = yield* DurableAlarmService;\n const config = yield* CloudflareDurableRuntimeConfig;\n const { ctx } = yield* DurableObjectContext;\n const failpoint = yield* ThreadMaintenanceFailpoint;\n\n /**\n * Consecutive no-progress passes — an in-memory CACHE, not state: a fresh incarnation\n * restarts at zero and merely re-arms sooner than a long-lived one would have.\n */\n const stalls = yield* Ref.make(0);\n const mutations = yield* ThreadMutationGate;\n const publication = yield* ThreadPublication;\n const projection = yield* ThreadProjectionMaintenance;\n const messages = yield* ThreadMessageDelivery;\n const host = yield* ThreadHostMaintenance;\n\n // A broken disposable index still needs a retry alarm and must not prevent startup.\n const projectionDeadline = projection.pendingDeadline.pipe(\n Effect.catchCauseIf(\n (cause) => !Cause.hasInterrupts(cause),\n (cause) =>\n Effect.logError(\"Thread projection deadline unavailable\", cause).pipe(\n Effect.as(Option.some(0)),\n ),\n ),\n );\n\n const pendingDeadline = Effect.gen(function* () {\n return earliestDeadline(\n earliestDeadline(yield* publication.pendingDeadline, yield* messages.pendingDeadline),\n earliestDeadline(yield* projectionDeadline, yield* host.pendingDeadline),\n );\n });\n\n const maintenancePassGate = yield* Semaphore.make(1);\n const minimumAlarmDelay = Math.max(1, Math.ceil(config.alarmBackoffBase / 2));\n\n const runTransaction = yield* makeStorageOperation;\n\n const ensureAlarm = Effect.fn(\"ThreadMaintenance.ensureAlarm\")(function* () {\n yield* failpoint.hit(\"maintenance:ensure:before\");\n const now = yield* Clock.currentTimeMillis;\n\n yield* runTransaction(\"ensure maintenance alarm\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state, initialized } = await readMaintenanceState(transaction);\n\n if (!initialized) {\n await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(state));\n }\n if (state.dirty > state.processed) {\n await ensureTransactionAlarmBy(transaction, now + config.wakeScanInterval);\n }\n }),\n );\n const deadline = yield* pendingDeadline;\n\n if (Option.isSome(deadline)) {\n yield* runTransaction(\"ensure publication alarm\", () =>\n ctx.storage.transaction((transaction) =>\n ensureTransactionAlarmBy(\n transaction,\n Math.max(now + minimumAlarmDelay, deadline.value),\n ),\n ),\n );\n }\n yield* failpoint.hit(\"maintenance:ensure:after\");\n });\n\n const beginPass = Effect.fn(\"ThreadMaintenance.beginPass\")(function* () {\n yield* failpoint.hit(\"maintenance:begin:before\");\n const now = yield* Clock.currentTimeMillis;\n\n const result = yield* runTransaction(\"begin maintenance pass\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state, initialized } = await readMaintenanceState(transaction);\n\n if (!initialized) {\n await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(state));\n }\n if (state.processed >= state.dirty) {\n // Prearm even a publication-only pass before invoking any host hook.\n await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);\n\n return {\n _tag: \"CaughtUp\" as const,\n nonterminal: state.nonterminal,\n };\n }\n // Pre-arm the earliest retry before recovery. A successful finish may move this slot\n // LATER to its bounded backoff, which does not cancel the running handler.\n await ensureTransactionAlarmBy(transaction, now + minimumAlarmDelay);\n\n return {\n _tag: \"Actionable\" as const,\n generation: state.dirty,\n nonterminal: state.nonterminal,\n };\n }),\n );\n\n yield* failpoint.hit(\"maintenance:begin:after\");\n\n return result;\n });\n\n const rearmDelay = Effect.fn(\"ThreadMaintenance.rearmDelay\")(function* (progressed: boolean) {\n const priorStalls = yield* Ref.getAndUpdate(stalls, (count) =>\n progressed ? 0 : count + 1,\n );\n\n if (progressed) return config.alarmBackoffBase;\n const exponent = Math.min(priorStalls, 30);\n const backoff = Math.min(config.alarmBackoffCap, config.alarmBackoffBase * 2 ** exponent);\n const jitter = yield* Random.next;\n // Full jitter over [backoff/2, backoff]: desynchronizes retry storms without ever\n // waiting longer than the deterministic bound.\n const jittered = Math.ceil(backoff / 2 + (backoff / 2) * jitter);\n\n return Math.min(jittered, config.wakeScanInterval);\n });\n\n const pass = Effect.fn(\"ThreadMaintenance.pass\")(function* (\n yieldAfter: DateTime.Utc,\n dispatchUntil: DateTime.Utc,\n ): Effect.fn.Return<MaintenancePassReport, MaintenancePassFailure, Scope.Scope> {\n const annotate = (report: MaintenancePassReport) =>\n Effect.annotateCurrentSpan({\n phase: report.phase,\n recovered: report.recovered,\n settled: report.settled,\n nonterminal: report.nonterminal,\n alarm: report.alarm,\n }).pipe(Effect.as(report));\n\n const started = yield* mutations.withSnapshot((activeAtStart) =>\n Effect.gen(function* () {\n const generation = yield* beginPass();\n\n if (generation._tag === \"Actionable\" && activeAtStart === 0) {\n // The gate excludes a producer starting between the snapshot and certification.\n yield* publication.prepareGeneration(generation.generation);\n }\n\n return { ...generation, activeAtStart };\n }),\n );\n\n // This scope owns auxiliary dispatch and listeners, independently of source completion.\n // Close it before acknowledgement, including on failure or event interruption.\n const auxiliaryScope = yield* Effect.acquireRelease(Scope.make(\"parallel\"), (scope, exit) =>\n Scope.close(scope, exit),\n );\n\n const sourceFinished = yield* Deferred.make<void>();\n const finished = Deferred.await(sourceFinished);\n\n // Fork setup too: an ordinary auxiliary setup failure is reported after native work,\n // rather than gating its opportunity. Event interruption still closes every fiber.\n const deliveryFiber = yield* Effect.forkIn(\n Scope.provide(auxiliaryScope)(messages.drainUntil(finished, dispatchUntil)),\n auxiliaryScope,\n );\n\n const hostFiber = yield* Effect.forkIn(\n Scope.provide(auxiliaryScope)(\n Effect.gen(function* () {\n yield* Schema.decodeEffect(AuxiliaryDispatchMillis)(host.dispatchTimeoutMillis).pipe(\n Effect.mapError((cause) =>\n DurableAlarmError.make({\n operation: \"host dispatch allowance\",\n message:\n \"Declare an integer whole-wave allowance between 1 and 300000 milliseconds\",\n cause,\n }),\n ),\n );\n yield* host.drainUntil(finished, dispatchUntil);\n }),\n ),\n auxiliaryScope,\n );\n\n // Backfill is one disposable wave; its timer starts beside native execution.\n const backfill = yield* Effect.forkIn(\n drainDue.pipe(\n Effect.provideService(ThreadProjectionMaintenance, projection),\n Effect.timeoutOption(config.projectionDispatchTimeoutMillis),\n ),\n auxiliaryScope,\n );\n\n const finishAuxiliary = Effect.gen(function* () {\n yield* Deferred.succeed(sourceFinished, undefined);\n\n const remaining = Math.max(\n 1,\n DateTime.toEpochMillis(dispatchUntil) - (yield* Clock.currentTimeMillis),\n );\n\n const hostJoin = yield* Effect.forkIn(\n Fiber.join(hostFiber).pipe(\n Effect.timeoutOption(Math.min(host.dispatchTimeoutMillis, remaining)),\n Effect.tap((result) =>\n Effect.annotateCurrentSpan({ \"host.timedOut\": Option.isNone(result) }),\n ),\n ),\n auxiliaryScope,\n );\n\n // Native transport uses the driver's actual Claim deadline, through Retry persistence.\n // Only our host/backfill timers suppress their own interruption. Earlier failed exits,\n // including pure self-interruption, remain failures while siblings are still blocked.\n yield* Fiber.joinAll([deliveryFiber, hostJoin, backfill]);\n yield* Scope.close(auxiliaryScope, Exit.void);\n });\n\n const deadline = yield* publication.pendingDeadline;\n\n if (\n started._tag === \"Actionable\" ||\n (Option.isSome(deadline) && deadline.value <= (yield* Clock.currentTimeMillis))\n ) {\n yield* publication.drain;\n }\n const pending = yield* publication.pendingDeadline;\n\n if (started._tag === \"CaughtUp\" || Option.isSome(pending)) {\n yield* finishAuxiliary;\n yield* failpoint.hit(\"maintenance:finish:before\");\n\n const disposition = yield* mutations.withSnapshot((active) =>\n Effect.gen(function* () {\n // Re-read under the producer gate: a concurrent append/host mutation cannot be\n // cleared using a stale empty deadline. Dirty generations bound all producer races.\n const latest = yield* pendingDeadline;\n const now = yield* Clock.currentTimeMillis;\n\n return yield* runTransaction(\"finish publication pass\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state } = await readMaintenanceState(transaction);\n\n const nativeDeadline =\n active > 0 || state.dirty > state.processed\n ? now + config.wakeScanInterval\n : Infinity;\n\n const next = Option.isSome(latest)\n ? Math.min(nativeDeadline, latest.value)\n : nativeDeadline;\n\n if (Number.isFinite(next)) {\n await transaction.setAlarm(Math.max(now + minimumAlarmDelay, next));\n\n return \"rearmed\" as const;\n }\n await transaction.deleteAlarm();\n\n return \"cleared\" as const;\n }),\n );\n }),\n );\n\n yield* failpoint.hit(\"maintenance:finish:after\");\n\n return yield* annotate(\n MaintenancePassReport.make({\n phase: \"caught-up\",\n recovered: 0,\n settled: 0,\n nonterminal: started.nonterminal,\n alarm: disposition,\n }),\n );\n }\n // Step 2 — reconciliation strictly precedes new work in this pass (exit gate).\n const recovered: ReadonlyArray<RecoveryReport> = yield* runtime.runRecovery;\n const reports = new Map(recovered.map((report) => [report.submissionId, report]));\n const current = yield* Stream.runCollect(ledger.scanNonterminal);\n const heads = new Map<ThreadId, SubmissionSnapshot>();\n\n for (const row of current) {\n if (!heads.has(row.threadId)) heads.set(row.threadId, row);\n }\n\n const eligible = [...heads.values()]\n .filter((head) => !stableExternalWait(head, reports))\n .map((head) => head.threadId)\n .sort();\n\n let selected = eligible[0];\n\n if (heads.size > 1 && selected !== undefined) {\n yield* failpoint.hit(\"maintenance:select:before\");\n selected = yield* runTransaction(\"select maintenance lane\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state } = await readMaintenanceState(transaction);\n\n const next =\n eligible.find(\n (threadId) =>\n state.lastServedThreadId === undefined || threadId > state.lastServedThreadId,\n ) ?? eligible[0];\n\n if (next !== undefined) {\n // Persist before the Attempt so an eviction or repeated yield cannot\n // monopolize the first lane. The generation and prearmed alarm survive.\n await transaction.put(\n MAINTENANCE_STATE_KEY,\n encodeMaintenanceState(\n ThreadMaintenanceState.make({ ...state, lastServedThreadId: next }),\n ),\n );\n }\n\n return next;\n }),\n );\n yield* failpoint.hit(\"maintenance:select:after\");\n }\n\n // One FIFO head per event, across all local lanes. The runtime keeps its normal\n // bounded Attempt and recovery contracts; followers belong to another alarm.\n const settlement =\n selected === undefined\n ? Option.none()\n : yield* runtime.processThreadHead(selected, { yieldAfter });\n\n yield* finishAuxiliary;\n const remaining = yield* Stream.runCollect(ledger.scanNonterminal);\n const waitingHeads = new Map<ThreadId, boolean>();\n\n const autonomous = remaining.some((snapshot) => {\n const headWaiting = waitingHeads.get(snapshot.threadId);\n\n if (headWaiting === undefined)\n waitingHeads.set(snapshot.threadId, stableExternalWait(snapshot, reports));\n // FIFO followers cannot execute through a stable external wait. Only plain queued\n // input is dormant here; admission repairs and accepted aborts still need a pass.\n if (\n headWaiting === true &&\n snapshot.state === \"ready\" &&\n reports.get(snapshot.submissionId)?.decision._tag === \"ApplyInput\"\n )\n return false;\n\n return !stableExternalWait(snapshot, reports);\n });\n\n const progressed =\n Option.isSome(settlement) ||\n recovered.some((report) => report.disposition === \"repaired\");\n\n const delay = autonomous ? yield* rearmDelay(progressed) : 0;\n const now = yield* Clock.currentTimeMillis;\n\n yield* failpoint.hit(\"maintenance:finish:before\");\n\n const alarmDisposition = yield* mutations.withSnapshot((active) =>\n Effect.gen(function* () {\n const publicationDeadline = yield* pendingDeadline;\n\n return yield* runTransaction(\"finish maintenance pass\", () =>\n ctx.storage.transaction(async (transaction) => {\n const { state } = await readMaintenanceState(transaction);\n\n // Autonomous work and in-flight mutations intentionally leave the observed\n // generation dirty. Otherwise acknowledge only the pass-start generation.\n const processed =\n autonomous || started.activeAtStart > 0 || active > 0\n ? state.processed\n : state.processed > started.generation\n ? state.processed\n : started.generation;\n\n const next = ThreadMaintenanceState.make({\n ...state,\n processed,\n nonterminal: remaining.length,\n });\n\n await transaction.put(MAINTENANCE_STATE_KEY, encodeMaintenanceState(next));\n if (autonomous) {\n // Replace the crash-fallback slot with this pass's bounded backoff. The target\n // is never earlier than the begin-pass fallback, so workerd does not cancel\n // this running alarm handler before its report/span can complete.\n await transaction.setAlarm(\n Option.isSome(publicationDeadline)\n ? Math.max(\n now + minimumAlarmDelay,\n Math.min(now + delay, publicationDeadline.value),\n )\n : now + delay,\n );\n\n return \"rearmed\" as const;\n }\n if (started.activeAtStart > 0 || active > 0 || next.dirty > next.processed) {\n // A mutation overlapped this pass's observation window or raced\n // acknowledgement. It stays dirty and its pre-armed bounded alarm survives;\n // unseen effects are never acknowledged. Do not accelerate that future alarm\n // from inside the current handler: workerd cancels a running handler when it\n // writes an earlier slot.\n await ensureTransactionAlarmBy(\n transaction,\n Option.isSome(publicationDeadline)\n ? Math.max(\n now + minimumAlarmDelay,\n Math.min(now + config.wakeScanInterval, publicationDeadline.value),\n )\n : now + config.wakeScanInterval,\n );\n\n return \"rearmed\" as const;\n }\n if (Option.isSome(publicationDeadline)) {\n await transaction.setAlarm(\n Math.max(now + minimumAlarmDelay, publicationDeadline.value),\n );\n\n return \"rearmed\" as const;\n }\n await transaction.deleteAlarm();\n\n return \"cleared\" as const;\n }),\n );\n }),\n );\n\n yield* failpoint.hit(\"maintenance:finish:after\");\n if (alarmDisposition === \"cleared\") {\n yield* Ref.set(stalls, 0);\n }\n\n return yield* annotate(\n MaintenancePassReport.make({\n phase: \"actionable\",\n recovered: recovered.length,\n settled: Option.isSome(settlement) ? 1 : 0,\n nonterminal: remaining.length,\n alarm: alarmDisposition,\n }),\n );\n });\n\n return ThreadMaintenance.of({\n // A mid-pass immediate hint is droppable; durable dirty state decides the final alarm.\n pass: Effect.gen(function* () {\n const now = yield* Clock.currentTimeMillis;\n const yieldAfter = DateTime.makeUnsafe(now + 10 * 60_000);\n const dispatchUntil = DateTime.makeUnsafe(now + 14 * 60_000);\n\n return yield* alarm.withWakesDeferred(\n maintenancePassGate.withPermit(Effect.scoped(pass(yieldAfter, dispatchUntil))),\n );\n }).pipe(\n // Include permit waiting, recovery and acknowledgement in the event deadline.\n // Interruption releases Attempt ownership, leaving the prearmed dirty generation\n // for recovery. It never changes the logical Run duration or settles a policy failure.\n // This cooperative timer cannot preempt synchronous CPU work or stuck finalizers.\n Effect.timeoutOrElse({\n duration: \"14 minutes\",\n orElse: () =>\n DurableAlarmError.make({\n operation: \"maintenance pass deadline\",\n message:\n \"The maintenance event exceeded its 14 minute deadline; durable recovery remains pending\",\n }),\n }),\n ),\n ensureAlarm: mutations.withSnapshot(() => ensureAlarm()),\n withMutation: (body) =>\n mutations.withMutation(\n body.pipe(\n Effect.tap(() =>\n publishCommitted.pipe(Effect.provideService(ThreadPublication, publication)),\n ),\n ),\n ),\n });\n }),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Context, Schema } from "effect";
|
|
2
2
|
declare namespace CloudflareConfig_d_exports {
|
|
3
|
-
export { AdmissionLimitExceeded, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CloudflareAdmissionLimitsValue, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError, DEFAULT_MAX_DATABASE_BYTES };
|
|
3
|
+
export { AdmissionLimitExceeded, AuxiliaryDispatchMillis, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CloudflareAdmissionLimitsValue, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError, DEFAULT_MAX_DATABASE_BYTES };
|
|
4
4
|
}
|
|
5
|
+
/** Maximum supported auxiliary wave equals the native message policy's five-minute ceiling. */
|
|
6
|
+
declare const AuxiliaryDispatchMillis: Schema.Int;
|
|
5
7
|
declare const CloudflarePlatformConfigError_base: Schema.Class<CloudflarePlatformConfigError, Schema.TaggedStruct<"CloudflarePlatformConfigError", {
|
|
6
8
|
readonly message: Schema.String;
|
|
7
9
|
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
@@ -64,6 +66,8 @@ declare const CloudflareDurableRuntimeConfigValue_base: Schema.Class<CloudflareD
|
|
|
64
66
|
readonly abortPollInterval: Schema.Int;
|
|
65
67
|
/** Canonical observation poll cadence of the Durable Object store. */
|
|
66
68
|
readonly observationPollInterval: Schema.Int;
|
|
69
|
+
/** One disposable projection backfill wave; independent of required live applyCommitted. */
|
|
70
|
+
readonly projectionDispatchTimeoutMillis: Schema.Int;
|
|
67
71
|
/** Per-value byte bound; must stay under the platform's 2 MB SQLite value limit. */
|
|
68
72
|
readonly maxStoredValueBytes: Schema.Int;
|
|
69
73
|
/** Opt-in full payload/digest-chain audit while opening the store. */
|
|
@@ -90,6 +94,7 @@ declare const CLOUDFLARE_RUNTIME_DEFAULTS: {
|
|
|
90
94
|
readonly leaseRenewalInterval: 10000;
|
|
91
95
|
readonly abortPollInterval: 500;
|
|
92
96
|
readonly observationPollInterval: 25;
|
|
97
|
+
readonly projectionDispatchTimeoutMillis: 30000;
|
|
93
98
|
readonly maxStoredValueBytes: 1900000;
|
|
94
99
|
readonly verifyOnOpen: false;
|
|
95
100
|
readonly maxQueueDepthPerLane: 256;
|
|
@@ -97,5 +102,5 @@ declare const CLOUDFLARE_RUNTIME_DEFAULTS: {
|
|
|
97
102
|
readonly maxDatabaseBytes: 9000000000;
|
|
98
103
|
};
|
|
99
104
|
//#endregion
|
|
100
|
-
export {
|
|
101
|
-
//# sourceMappingURL=CloudflareConfig-
|
|
105
|
+
export { CloudflareAdmissionLimitsValue as a, CloudflareDurableRuntimeConfigValue as c, CLOUDFLARE_RUNTIME_DEFAULTS as i, CloudflarePlatformConfigError as l, AuxiliaryDispatchMillis as n, CloudflareConfig_d_exports as o, CLOUDFLARE_DATABASE_CAP_BYTES as r, CloudflareDurableRuntimeConfig as s, AdmissionLimitExceeded as t, DEFAULT_MAX_DATABASE_BYTES as u };
|
|
106
|
+
//# sourceMappingURL=CloudflareConfig-D472JOQA.d.mts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { AdmissionLimitExceeded, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CloudflareAdmissionLimitsValue, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError, DEFAULT_MAX_DATABASE_BYTES };
|
|
1
|
+
import { a as CloudflareAdmissionLimitsValue, c as CloudflareDurableRuntimeConfigValue, i as CLOUDFLARE_RUNTIME_DEFAULTS, l as CloudflarePlatformConfigError, n as AuxiliaryDispatchMillis, r as CLOUDFLARE_DATABASE_CAP_BYTES, s as CloudflareDurableRuntimeConfig, t as AdmissionLimitExceeded, u as DEFAULT_MAX_DATABASE_BYTES } from "./CloudflareConfig-D472JOQA.mjs";
|
|
2
|
+
export { AdmissionLimitExceeded, AuxiliaryDispatchMillis, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CloudflareAdmissionLimitsValue, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError, DEFAULT_MAX_DATABASE_BYTES };
|