@effect-agent/platform-cloudflare 0.1.0-beta.45 → 0.1.0-beta.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/Alarm.d.mts +119 -0
  2. package/dist/Alarm.mjs +317 -0
  3. package/dist/Alarm.mjs.map +1 -0
  4. package/dist/{browser-rest-capture.d.mts → BrowserRestCapture.d.mts} +5 -4
  5. package/dist/{browser-rest-capture.mjs → BrowserRestCapture.mjs} +5 -4
  6. package/dist/BrowserRestCapture.mjs.map +1 -0
  7. package/dist/{browser-rest-crawl.d.mts → BrowserRestCrawl.d.mts} +4 -3
  8. package/dist/{browser-rest-crawl.mjs → BrowserRestCrawl.mjs} +4 -3
  9. package/dist/BrowserRestCrawl.mjs.map +1 -0
  10. package/dist/{bindings-BrVEOkfe.d.mts → CloudflareBindings.d.mts} +7 -5
  11. package/dist/CloudflareBindings.mjs +98 -0
  12. package/dist/CloudflareBindings.mjs.map +1 -0
  13. package/dist/{browser-quick-action.mjs → CloudflareBrowser-1jeZx3_g.mjs} +19 -4
  14. package/dist/CloudflareBrowser-1jeZx3_g.mjs.map +1 -0
  15. package/dist/{browser-quick-action.d.mts → CloudflareBrowser-DJq53niJ.d.mts} +9 -18
  16. package/dist/CloudflareBrowser.d.mts +2 -0
  17. package/dist/CloudflareBrowser.mjs +2 -0
  18. package/dist/CloudflareCodeMode.d.mts +44 -0
  19. package/dist/CloudflareCodeMode.mjs +618 -0
  20. package/dist/CloudflareCodeMode.mjs.map +1 -0
  21. package/dist/CloudflareConfig-f3CqTel1.d.mts +101 -0
  22. package/dist/CloudflareConfig.d.mts +2 -0
  23. package/dist/CloudflareConfig.mjs +122 -0
  24. package/dist/CloudflareConfig.mjs.map +1 -0
  25. package/dist/CloudflareMemory.d.mts +140 -0
  26. package/dist/CloudflareMemory.mjs +168 -0
  27. package/dist/CloudflareMemory.mjs.map +1 -0
  28. package/dist/{scheduling.d.mts → CloudflareScheduling.d.mts} +9 -6
  29. package/dist/{scheduling.mjs → CloudflareScheduling.mjs} +22 -8
  30. package/dist/CloudflareScheduling.mjs.map +1 -0
  31. package/dist/{subscriptions.d.mts → CloudflareSubscriptions.d.mts} +11 -6
  32. package/dist/{subscriptions.mjs → CloudflareSubscriptions.mjs} +26 -7
  33. package/dist/CloudflareSubscriptions.mjs.map +1 -0
  34. package/dist/CloudflareThreadClient.d.mts +715 -0
  35. package/dist/CloudflareThreadClient.mjs +364 -0
  36. package/dist/CloudflareThreadClient.mjs.map +1 -0
  37. package/dist/{interactive-browser.d.mts → InteractiveBrowser.d.mts} +5 -4
  38. package/dist/{interactive-browser.mjs → InteractiveBrowser.mjs} +6 -4
  39. package/dist/InteractiveBrowser.mjs.map +1 -0
  40. package/dist/{protected-browser.d.mts → ProtectedBrowser.d.mts} +4 -3
  41. package/dist/{protected-browser.mjs → ProtectedBrowser.mjs} +4 -3
  42. package/dist/ProtectedBrowser.mjs.map +1 -0
  43. package/dist/ThreadObject-Ia94R_B1.d.mts +735 -0
  44. package/dist/ThreadObject-IrGuIO4a.mjs +575 -0
  45. package/dist/ThreadObject-IrGuIO4a.mjs.map +1 -0
  46. package/dist/ThreadObject.d.mts +2 -0
  47. package/dist/ThreadObject.mjs +3 -0
  48. package/dist/WakeScheduler.d.mts +23 -0
  49. package/dist/WakeScheduler.mjs +60 -0
  50. package/dist/WakeScheduler.mjs.map +1 -0
  51. package/dist/boundary-BguazVkh.mjs +42 -0
  52. package/dist/boundary-BguazVkh.mjs.map +1 -0
  53. package/dist/{browser-session-lifecycle-ZGgb3pnK.mjs → browser-session-lifecycle-CUbVfEgP.mjs} +2 -2
  54. package/dist/browser-session-lifecycle-CUbVfEgP.mjs.map +1 -0
  55. package/dist/{browser-session-lifecycle-DqntvG-Y.d.mts → browser-session-lifecycle-DJpqLXy_.d.mts} +2 -2
  56. package/dist/index.d.mts +12 -1833
  57. package/dist/index.mjs +12 -1357
  58. package/dist/prepared-admission-DRBhl2Sp.mjs +63 -0
  59. package/dist/prepared-admission-DRBhl2Sp.mjs.map +1 -0
  60. package/package.json +1 -1
  61. package/src/{alarm.ts → Alarm.ts} +6 -7
  62. package/src/{browser-rest-capture.ts → BrowserRestCapture.ts} +3 -3
  63. package/src/{browser-rest-crawl.ts → BrowserRestCrawl.ts} +2 -2
  64. package/src/{bindings.ts → CloudflareBindings.ts} +2 -2
  65. package/src/CloudflareBrowser.ts +15 -0
  66. package/src/{code-mode-executor.ts → CloudflareCodeMode.ts} +3 -3
  67. package/src/{config.ts → CloudflareConfig.ts} +3 -2
  68. package/src/{memory.ts → CloudflareMemory.ts} +15 -12
  69. package/src/{scheduling.ts → CloudflareScheduling.ts} +12 -11
  70. package/src/{subscriptions.ts → CloudflareSubscriptions.ts} +14 -12
  71. package/src/{client.ts → CloudflareThreadClient.ts} +27 -20
  72. package/src/{interactive-browser.ts → InteractiveBrowser.ts} +9 -5
  73. package/src/{thread-object.ts → ThreadObject.ts} +37 -29
  74. package/src/{wake-scheduler.ts → WakeScheduler.ts} +5 -5
  75. package/src/index.ts +11 -30
  76. package/src/{browser-quick-action.ts → internal/browser-quick-action.ts} +10 -8
  77. package/src/{layers.ts → internal/layers.ts} +39 -30
  78. package/src/{prepared-admission.ts → internal/prepared-admission.ts} +7 -7
  79. package/src/{transport.ts → internal/transport.ts} +5 -2
  80. package/src/protected-browser/binding.ts +3 -2
  81. package/src/protected-browser/native.ts +2 -2
  82. package/src/protected-browser/policy.ts +2 -2
  83. package/dist/browser-quick-action.mjs.map +0 -1
  84. package/dist/browser-rest-capture.mjs.map +0 -1
  85. package/dist/browser-rest-crawl.mjs.map +0 -1
  86. package/dist/browser-session-lifecycle-ZGgb3pnK.mjs.map +0 -1
  87. package/dist/index.mjs.map +0 -1
  88. package/dist/interactive-browser.mjs.map +0 -1
  89. package/dist/prepared-admission-BhW2eT_a.mjs +0 -890
  90. package/dist/prepared-admission-BhW2eT_a.mjs.map +0 -1
  91. package/dist/protected-browser.mjs.map +0 -1
  92. package/dist/scheduling.mjs.map +0 -1
  93. package/dist/subscriptions.mjs.map +0 -1
  94. /package/src/{protected-browser.ts → ProtectedBrowser.ts} +0 -0
  95. /package/src/{boundary.ts → internal/boundary.ts} +0 -0
  96. /package/src/{browser-session-lifecycle.ts → internal/browser-session-lifecycle.ts} +0 -0
  97. /package/src/{progress-wait.ts → internal/progress-wait.ts} +0 -0
package/dist/index.mjs CHANGED
@@ -1,1357 +1,12 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { $ as CloudflarePlatformConfigError, A as decodeUnknownResolutionCommand, B as DurableAlarmError, C as decodeApprovalDecisionCommand, D as decodeObservePageRequest, E as decodeHostResponse, F as encodeHostResponse, G as safeCauseDiagnostic, H as MaintenancePassReport, I as encodeObservePageRequest, J as CLOUDFLARE_DATABASE_CAP_BYTES, K as safeCauseMessage, L as encodeReceipt, M as encodeApprovalDecisionCommand, N as encodeAwaitProgressRequest, O as decodeReceipt, P as encodeCancelProgressRequest, Q as CloudflareDurableRuntimeConfigValue, R as encodeSubmitRequest, S as decodeAbortCommand, T as decodeCancelProgressRequest, U as ThreadMaintenance, V as DurableAlarmService, W as ThreadMaintenanceFailpoint, X as CloudflareAdmissionLimitsValue, Y as CLOUDFLARE_RUNTIME_DEFAULTS, Z as CloudflareDurableRuntimeConfig, _ as SubmitRequest, a as AwaitProgressRequest, at as threadNamespaceFromEnv, b as UnknownResolutionRecorded, c as HostFailed, d as HostResponse, et as DEFAULT_MAX_DATABASE_BYTES, f as ObservePageRequest, g as SettlementReached, h as ProgressObserved, i as ApprovalRecorded, it as ThreadObjectNamespace, j as encodeAbortCommand, k as decodeSubmitRequest, l as HostFailure, m as ProgressCancelled, n as cloudflareScheduledInputAdmissionLayer, nt as DurableObjectContext, o as CancelProgressRequest, ot as threadNamespaceLayer, p as ObservedPage, q as AdmissionLimitExceeded, r as AbortRecorded, rt as ThreadObjectIdentity, s as CloudflareThreadClient, t as cloudflarePreparedInputAdmissionLayer, tt as CloudflareBindingError, u as HostProtocolError, v as SubmitSucceeded, w as decodeAwaitProgressRequest, x as boundHostDiagnostic, y as ThreadClientError, z as encodeUnknownResolutionCommand } from "./prepared-admission-BhW2eT_a.mjs";
3
- import { CloudflareSchedulingClient, ScheduleAlarmProtocolError, ScheduleOwnerIdentity, ScheduleOwnerNamespace, ScheduleOwnerProtocolError, makeScheduleOwnerObjectClass } from "./scheduling.mjs";
4
- import { CloudflareSubscriptionConfigError, CloudflareSubscriptionsClient, SubscriptionAlarmProtocolError, SubscriptionPartitionIdentity, SubscriptionPartitionNamespace, SubscriptionPartitionProtocolError, makeSubscriptionPartitionObjectClass, sourcePartitionName, validateCloudflareSubscriptionLimits } from "./subscriptions.mjs";
5
- import { CloudflareBrowser } from "./browser-quick-action.mjs";
6
- import { Clock, Context, Deferred, Duration, Effect, Exit, Fiber, Layer, Option, PubSub, Queue, Ref, Schema, Stream } from "effect";
7
- import { MemoryOwnerIdentity, MemoryOwnerRequest, MemoryOwnerResponse, MemoryRpcError, MemoryRpcLimits, ThreadPortTransport, decodeMemoryWire, decodePortRequest, defaultDoMemoryStorageLimits, defaultMemoryRpcLimits, doMemoryStoreLayerWithFailpoints, encodeMemoryWire, encodePortResponse, executePortRequest, handleMemoryOwnerRequest, portTransportFailure, routedSubmissionLedgerLayer, routedThreadStoreLayer, storageConfigLayer, storageFailpointLayer, submissionLedgerLayer, threadStoreLayer } from "@effect-agent/storage-cloudflare";
8
- import { AppendConflict, DigestError, DurableAgentRuntime, DurableRuntimeConfig, DurableRuntimeFailpoint, DurableRuntimeFailpointError, FenceRejected, IntegrityReport, LedgerError, ObligationReport, ObligationThresholds, OperationAuthorizationRequest, OperationAuthorizer, OperationDenied, OwnershipLost, PersistedJson, Principal, ProducerId, RecoveryExplanation, RecoveryReport, RetryCommand, RetryRefused, RunJournalError, SettlementConflict, SubmissionLedger, SubmissionLookupByKey, ThreadNotMaterialized, ThreadRead, ThreadStore, ThreadStoreError, ToolReconciler, WakeScheduler, compileRegistrations, makeWakeSubscriptionHub, operationAuthorizerLayer } from "@effect-agent/thread";
9
- import { Memory, MemoryAccess, MemoryDocument, MemoryMutationFailpoint, MemoryNamespace, MemoryNamespaceAddress, MemoryRecallLimits, MemoryStorageError, MemoryWriter, SubmissionId, ThreadId } from "@effect-agent/core";
10
- import { CurrentToolFailureObserver, RunContextPreparationPassthrough, RunToolAuthorization, toolFailureObserverLayer } from "@effect-agent/engine";
11
- import { BrowserCrypto } from "@effect/platform-browser";
12
- import { SqliteClient } from "@effect/sql-sqlite-do";
13
- import { DurableObject, DurableObjectState, WorkerEnvironment } from "effect-cf";
14
- import { CodeExecutionHost, CodeExecutionProtocolError, CodeExecutionResourceUse, CodeExecutionResult, CodeExecutionTimeoutError, CodeExecutor, CodeExecutorStartError, CodeExecutorTerminatedError, CodeExecutorUnsupportedError, CodeHostCall, CodeHostCallLimitError, CodeHostCallResult, CodeOutputLimitError, CodeProgramFailedError, CodeSourceError, SandboxImplementation } from "@effect-agent/sandbox";
15
- import { RpcTarget } from "cloudflare:workers";
16
- //#region src/wake-scheduler.ts
17
- /**
18
- * Bounded in-memory wake buffer for same-incarnation `awaitSettlement` subscribers. Wake
19
- * hints are droppable by contract (consumers pair every subscription with ledger polls), so
20
- * a full buffer slides out the oldest hint and an eviction simply loses the buffer — the
21
- * poll interval and the persisted alarm keep liveness (persistence §14).
22
- */
23
- const WAKE_BUFFER_CAPACITY = 1024;
24
- /** A remote wake stub call failed; always swallowed and logged (hints are droppable). */
25
- var RemoteWakeDropped = class extends Schema.TaggedError()("RemoteWakeDropped", {
26
- threadId: Schema.String,
27
- message: Schema.String,
28
- cause: Schema.optionalKey(Schema.Defect())
29
- }) {};
30
- /**
31
- * The DC `WakeScheduler` (plan §1.4):
32
- *
33
- * - `notify(local)` → `setAlarm(now)` — durable, cheap, coalescing (the alarm slot keeps the
34
- * earliest deadline) — plus a same-incarnation PubSub hint for `awaitSettlement` waiters.
35
- * - `notify(remote)` → fire-and-forget `wake()` on the owning Object's stub with every error
36
- * swallowed and logged: hints are droppable, and the target's own alarm/scan pairing (the
37
- * maintenance pass re-polls unsettled children) guarantees liveness without this call.
38
- * - `wakes` → the bounded sliding PubSub only. No important state lives in memory: the
39
- * subscription accelerates waiters within one incarnation and the settlement poll interval
40
- * is the correctness path.
41
- */
42
- const cloudflareWakeSchedulerLayer = Layer.effect(WakeScheduler)(Effect.gen(function* () {
43
- const alarm = yield* DurableAlarmService;
44
- const identity = yield* ThreadObjectIdentity;
45
- const { namespace } = yield* ThreadObjectNamespace;
46
- const hints = yield* PubSub.sliding(WAKE_BUFFER_CAPACITY);
47
- const progress = yield* makeWakeSubscriptionHub;
48
- yield* Effect.addFinalizer(() => PubSub.shutdown(hints));
49
- const notifyLocal = (threadId) => progress.notify(threadId).pipe(Effect.andThen(PubSub.publish(hints, threadId)), Effect.andThen(alarm.scheduleNow), Effect.catch((error) => Effect.logWarning("CloudflareWakeScheduler: local alarm wake failed", error)), Effect.asVoid);
50
- const notifyRemote = (threadId) => Effect.tryPromise({
51
- try: () => namespace.get(namespace.idFromName(threadId)).wake(),
52
- catch: (cause) => RemoteWakeDropped.make({
53
- threadId,
54
- message: safeCauseMessage(cause, "The remote wake failed without a diagnostic"),
55
- cause
56
- })
57
- }).pipe(Effect.catch((error) => Effect.logWarning(`CloudflareWakeScheduler: remote wake of ${threadId} dropped`, error)), Effect.asVoid);
58
- return WakeScheduler.of({
59
- notify: (threadId) => threadId === identity.threadId ? notifyLocal(threadId) : notifyRemote(threadId),
60
- subscribe: progress.subscribe,
61
- wakes: Stream.fromPubSub(hints)
62
- });
63
- }));
64
- //#endregion
65
- //#region src/progress-wait.ts
66
- /** Cancellation tombstones are bounded hints, never durable authority. */
67
- const MAX_CANCELLATION_TOMBSTONES = 1024;
68
- /**
69
- * Per-incarnation cancellation registry for long-lived progress RPCs. The public runtime owns
70
- * the actual wake registration; this host-only registry lets an interrupted Worker Effect ask
71
- * the Object to interrupt its scoped wait before the Worker execution context itself ends.
72
- */
73
- var ProgressWaitRegistry = class ProgressWaitRegistry extends Context.Service()("@effect-agent/platform-cloudflare/ProgressWaitRegistry") {
74
- static layer = Layer.effect(ProgressWaitRegistry, Effect.gen(function* () {
75
- const registrations = yield* Ref.make(/* @__PURE__ */ new Map());
76
- const remove = (waiterId, deferred) => Ref.update(registrations, (current) => {
77
- const existing = current.get(waiterId);
78
- if (existing === void 0 || existing === "cancelled" || !existing.has(deferred)) return current;
79
- const next = new Map(current);
80
- const active = new Set(existing);
81
- active.delete(deferred);
82
- if (active.size === 0) next.delete(waiterId);
83
- else next.set(waiterId, active);
84
- return next;
85
- });
86
- const subscribe = Effect.fn("ProgressWaitRegistry.subscribe")((waiterId) => Effect.gen(function* () {
87
- const deferred = yield* Deferred.make();
88
- yield* Effect.addFinalizer(() => remove(waiterId, deferred));
89
- return {
90
- cancelled: yield* Ref.modify(registrations, (current) => {
91
- const existing = current.get(waiterId);
92
- const next = new Map(current);
93
- if (existing === "cancelled") return [true, current];
94
- const active = new Set(existing ?? []);
95
- active.add(deferred);
96
- next.set(waiterId, active);
97
- return [false, next];
98
- }),
99
- deferred
100
- };
101
- }).pipe(Effect.map(({ cancelled, deferred }) => cancelled ? Effect.void : Deferred.await(deferred))));
102
- const cancel = Effect.fn("ProgressWaitRegistry.cancel")(function* (waiterId) {
103
- const waiters = yield* Ref.modify(registrations, (current) => {
104
- const existing = current.get(waiterId);
105
- const next = new Map(current);
106
- if (existing === void 0) {
107
- next.set(waiterId, "cancelled");
108
- let tombstones = 0;
109
- for (const registration of next.values()) if (registration === "cancelled") tombstones += 1;
110
- if (tombstones > MAX_CANCELLATION_TOMBSTONES) for (const [id, registration] of next) {
111
- if (registration !== "cancelled") continue;
112
- next.delete(id);
113
- break;
114
- }
115
- return [[], next];
116
- }
117
- if (existing === "cancelled") return [[], current];
118
- next.delete(waiterId);
119
- return [[...existing], next];
120
- });
121
- yield* Effect.forEach(waiters, (waiter) => Deferred.succeed(waiter, void 0), { discard: true });
122
- });
123
- return ProgressWaitRegistry.of({
124
- subscribe,
125
- cancel
126
- });
127
- }));
128
- };
129
- //#endregion
130
- //#region src/transport.ts
131
- /**
132
- * `ThreadPortTransport` over native Durable Object JS RPC (decision D-P6-3): one
133
- * `portCall(envelope)` on the stub of the Object that owns the addressed Thread
134
- * (`namespace.idFromName(threadId)` — the identity rule, plan §1.2). The envelopes are
135
- * already Schema-encoded JSON, so the RPC boundary carries only structured-cloneable values;
136
- * the protocol module stays transport-agnostic and fetch-with-JSON remains the documented
137
- * fallback carrier.
138
- *
139
- * Every delivery problem — stub construction, RPC rejection, overload, deploy-in-progress —
140
- * surfaces as `PortTransportError` (preserving the platform stub's own `retryable` signal
141
- * when present) and NEVER as a fabricated answer: on `resolveAdmission` the routing layer
142
- * turns exactly this error into `AdmissionIndeterminate` (SUB-031).
143
- */
144
- const threadPortTransportLayer = Layer.effect(ThreadPortTransport)(Effect.gen(function* () {
145
- const { namespace } = yield* ThreadObjectNamespace;
146
- return ThreadPortTransport.of({ call: (threadId, request) => Effect.tryPromise({
147
- try: () => namespace.get(namespace.idFromName(threadId)).portCall(request),
148
- catch: (cause) => portTransportFailure(threadId, cause)
149
- }).pipe(Effect.withSpan("CloudflarePortTransport.call", { attributes: { threadId } })) });
150
- }));
151
- //#endregion
152
- //#region src/layers.ts
153
- /**
154
- * Owner-side execution port for a `portCall` request the wire endpoint has already decoded.
155
- * It executes against THIS Object's LOCAL port facets — never the routed decorators, so a
156
- * request cannot bounce between Objects — and returns the typed response for the endpoint to
157
- * encode.
158
- */
159
- var ThreadObjectPorts = class extends Context.Service()("@effect-agent/platform-cloudflare/ThreadObjectPorts") {};
160
- const decodeConfigValue = Schema.decodeUnknownEffect(CloudflareDurableRuntimeConfigValue);
161
- const decodeThreadId = Schema.decodeUnknownEffect(ThreadId);
162
- const decodeProducerId = Schema.decodeUnknownEffect(ProducerId);
163
- const configFromOptions = (options) => decodeConfigValue({
164
- deploymentId: options.deploymentId,
165
- producerPrefix: options.producerPrefix,
166
- ownershipLeaseDuration: options.ownershipLeaseDuration ?? CLOUDFLARE_RUNTIME_DEFAULTS.ownershipLeaseDuration,
167
- alarmBackoffBase: options.alarmBackoffBase ?? CLOUDFLARE_RUNTIME_DEFAULTS.alarmBackoffBase,
168
- alarmBackoffCap: options.alarmBackoffCap ?? CLOUDFLARE_RUNTIME_DEFAULTS.alarmBackoffCap,
169
- wakeScanInterval: options.wakeScanInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.wakeScanInterval,
170
- settlementPollInterval: options.settlementPollInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.settlementPollInterval,
171
- leaseRenewalInterval: options.leaseRenewalInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.leaseRenewalInterval,
172
- abortPollInterval: options.abortPollInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.abortPollInterval,
173
- observationPollInterval: options.observationPollInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.observationPollInterval,
174
- maxStoredValueBytes: options.maxStoredValueBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxStoredValueBytes,
175
- verifyOnOpen: options.verifyOnOpen ?? CLOUDFLARE_RUNTIME_DEFAULTS.verifyOnOpen,
176
- limits: {
177
- maxQueueDepthPerLane: options.maxQueueDepthPerLane ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxQueueDepthPerLane,
178
- maxInputBytes: Math.min(options.maxInputBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxInputBytes, options.maxStoredValueBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxStoredValueBytes),
179
- maxDatabaseBytes: options.maxDatabaseBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxDatabaseBytes
180
- }
181
- }).pipe(Effect.mapError((error) => CloudflarePlatformConfigError.make({
182
- message: `Invalid Cloudflare durable runtime configuration: ${error.message}`,
183
- cause: error
184
- })));
185
- /**
186
- * The Thread this Object owns, from the Object identity rule (plan §1.2): Thread
187
- * Objects are addressed exclusively by `idFromName(threadId)`, so `ctx.id.name` IS the
188
- * Thread ID. An unnamed Object (from `newUniqueId`) is a deployment error, not a lane.
189
- */
190
- const threadIdFromState = (ctx) => ctx.id.name === void 0 ? Effect.fail(CloudflarePlatformConfigError.make({ message: "This Durable Object was not created via idFromName(threadId); Thread Objects must be addressed by their Thread identity (plan §1.2)." })) : decodeThreadId(ctx.id.name).pipe(Effect.mapError((error) => CloudflarePlatformConfigError.make({
191
- message: `The Durable Object name is not a valid ThreadId: ${error.message}`,
192
- cause: error
193
- })));
194
- /**
195
- * Validate deployment settings and derive services before building application dependencies.
196
- * The native class factory builds this Layer inside its constructor gate. Custom Effect hosts
197
- * can provide it around the complete application Layer with the native context already supplied.
198
- */
199
- const layerConfig = (options) => Layer.unwrap(Effect.gen(function* () {
200
- const { ctx } = yield* DurableObjectContext;
201
- const config = yield* configFromOptions(options);
202
- const threadId = yield* threadIdFromState(ctx);
203
- const producerId = yield* decodeProducerId(`${config.producerPrefix}:${threadId}`).pipe(Effect.mapError((error) => CloudflarePlatformConfigError.make({
204
- message: `The minted producer identity is invalid: ${error.message}`,
205
- cause: error
206
- })));
207
- return Layer.mergeAll(Layer.succeed(CloudflareDurableRuntimeConfig, config), Layer.succeed(ThreadObjectIdentity, {
208
- threadId,
209
- producerId
210
- }), DurableRuntimeConfig.layer({
211
- deploymentId: config.deploymentId,
212
- producerId,
213
- settlementPollInterval: Duration.millis(config.settlementPollInterval),
214
- leaseRenewalInterval: Duration.millis(config.leaseRenewalInterval),
215
- abortPollInterval: Duration.millis(config.abortPollInterval),
216
- ...options.estimateCostMicrousd === void 0 ? {} : { estimateCostMicrousd: options.estimateCostMicrousd }
217
- }), BrowserCrypto.layer, storageFailpointLayer({
218
- storage: ctx.storage,
219
- failpoint: options.storageFailpoint?.(ctx)
220
- }), options.runtimeFailpoint === void 0 ? DurableRuntimeFailpoint.layer : Layer.succeed(DurableRuntimeFailpoint, { hit: options.runtimeFailpoint(ctx) }), options.maintenanceFailpoint === void 0 ? ThreadMaintenanceFailpoint.layer : Layer.succeed(ThreadMaintenanceFailpoint, { hit: options.maintenanceFailpoint(ctx) }), options.toolReconciler ?? ToolReconciler.uncertain, options.operationAuthorizer === void 0 ? Layer.empty : operationAuthorizerLayer(options.operationAuthorizer), options.toolFailureObserver === void 0 ? Layer.succeed(CurrentToolFailureObserver, void 0) : toolFailureObserverLayer(options.toolFailureObserver), RunContextPreparationPassthrough, RunToolAuthorization.allowAll);
221
- }));
222
- /**
223
- * Register typed Agents and version declarations. Hashing and dependency capture happen in
224
- * this Layer's Scope, after application Layers have been provided. Every Agent's instruction,
225
- * Tool, Schema, and model requirements remain visible until satisfied by Layer composition.
226
- * Use Layer.unwrap for registration values that need effectful application setup.
227
- */
228
- const layer = (registrations) => Layer.unwrap(Effect.map(compileRegistrations(registrations), layerFromBindings));
229
- /**
230
- * Assemble the durable runtime from already-resolved Agent Bindings.
231
- * Use `ThreadObject.layer` to compile typed Agent registrations instead.
232
- * Supply host services through `ThreadObject.make` or `ThreadObject.layerConfig` and
233
- * the Durable Object context and namespace Layers when composing a custom host.
234
- */
235
- const layerFromBindings = (bindings) => Layer.unwrap(Effect.gen(function* () {
236
- const { ctx } = yield* DurableObjectContext;
237
- const config = yield* CloudflareDurableRuntimeConfig;
238
- const { threadId } = yield* ThreadObjectIdentity;
239
- const storageOptions = {
240
- storage: ctx.storage,
241
- observationPollInterval: config.observationPollInterval,
242
- ownershipLeaseDuration: config.ownershipLeaseDuration,
243
- maxStoredValueBytes: config.maxStoredValueBytes,
244
- verifyOnOpen: config.verifyOnOpen
245
- };
246
- const infrastructure = Layer.mergeAll(storageConfigLayer(storageOptions), SqliteClient.layer({ storage: ctx.storage }));
247
- const localPorts = Layer.mergeAll(threadStoreLayer, submissionLedgerLayer).pipe(Layer.provide(infrastructure));
248
- const portsEndpointLayer = Layer.effect(ThreadObjectPorts)(Effect.gen(function* () {
249
- const local = yield* Effect.context();
250
- return ThreadObjectPorts.of({ handle: (request) => executePortRequest(request).pipe(Effect.provide(local)) });
251
- })).pipe(Layer.provide(localPorts));
252
- const routedPorts = Layer.mergeAll(routedSubmissionLedgerLayer({ localThreadId: threadId }), routedThreadStoreLayer({ localThreadId: threadId })).pipe(Layer.provide(localPorts), Layer.provide(threadPortTransportLayer));
253
- const base = Layer.mergeAll(DurableAlarmService.layer, ProgressWaitRegistry.layer);
254
- const runtimeStack = DurableAgentRuntime.layerWithBindings(bindings).pipe(Layer.provideMerge(routedPorts), Layer.provideMerge(cloudflareWakeSchedulerLayer), Layer.provideMerge(base));
255
- return Layer.mergeAll(runtimeStack, ThreadMaintenance.layer.pipe(Layer.provide(runtimeStack)), portsEndpointLayer);
256
- }));
257
- //#endregion
258
- //#region src/thread-object.ts
259
- var thread_object_exports = /* @__PURE__ */ __exportAll({
260
- AdminExplainRequest: () => AdminExplainRequest,
261
- AdminFailed: () => AdminFailed,
262
- AdminFailure: () => AdminFailure,
263
- AdminResponse: () => AdminResponse,
264
- AdminVerifyRequest: () => AdminVerifyRequest,
265
- ExplainedRecovery: () => ExplainedRecovery,
266
- ObligationsScanned: () => ObligationsScanned,
267
- RetryExecuted: () => RetryExecuted,
268
- VerifiedIntegrity: () => VerifiedIntegrity,
269
- decodeAdminExplainRequest: () => decodeAdminExplainRequest,
270
- decodeAdminResponse: () => decodeAdminResponse,
271
- decodeAdminVerifyRequest: () => decodeAdminVerifyRequest,
272
- decodeObligationThresholds: () => decodeObligationThresholds,
273
- decodeRetryCommand: () => decodeRetryCommand,
274
- encodeAdminResponse: () => encodeAdminResponse,
275
- layer: () => layer,
276
- layerConfig: () => layerConfig,
277
- make: () => make
278
- });
279
- /** Classify only a decoded port request so new protocol members cannot bypass pre-arming. */
280
- const isMutatingPortRequest = (request) => {
281
- switch (request._tag) {
282
- case "LedgerAdmit":
283
- case "LedgerMarkReady":
284
- case "LedgerRequestAbort":
285
- case "LedgerRecordChildSettled":
286
- case "StoreMaterialize":
287
- case "StoreAppend": return true;
288
- case "LedgerLookup":
289
- case "LedgerResolveAdmission":
290
- case "StoreReadPage":
291
- case "StoreInspectTail":
292
- case "StoreExport": return false;
293
- }
294
- return false;
295
- };
296
- /** The literal encoded `PortFailed(PortProtocolError)` fallback (same shape as WP2's). */
297
- const encodedPortProtocolFailure = (message) => ({
298
- _tag: "PortFailed",
299
- failure: {
300
- _tag: "PortProtocolError",
301
- message: boundHostDiagnostic(message)
302
- }
303
- });
304
- const protocolFailure = (context) => (error) => HostProtocolError.make({ message: boundHostDiagnostic(`${context}: ${error.message}`) });
305
- /** Fold one endpoint's typed failures into the uniform `HostResponse` envelope. */
306
- const respond = (effect) => effect.pipe(Effect.map((result) => result), Effect.catch((failure) => Effect.succeed(HostFailed.make({ failure }))));
307
- /** Encode the response envelope; an unencodable response degrades to a protocol failure. */
308
- const encodeResponse = (response) => encodeHostResponse(response).pipe(Effect.catch((error) => Effect.succeed({
309
- _tag: "HostFailed",
310
- failure: {
311
- _tag: "HostProtocolError",
312
- message: boundHostDiagnostic(`The host response could not be encoded: ${error.message}`)
313
- }
314
- })));
315
- const utf8Bytes = (value) => new TextEncoder().encode(JSON.stringify(value)).length;
316
- /**
317
- * The admission-limits gate, BEFORE `runtime.submit` touches the ledger (exit gate
318
- * "resource limits are checked before admission"; DEPLOY-007). A replayed idempotency key is
319
- * exempt: its accepted-work obligation already exists, and returning the original Receipt
320
- * consumes no new quota. Refusals are typed `AdmissionLimitExceeded` and nothing is written.
321
- */
322
- const gateAdmissionLimits = Effect.fn("ThreadObject.gateAdmissionLimits")(function* (request) {
323
- const identity = yield* ThreadObjectIdentity;
324
- const config = yield* CloudflareDurableRuntimeConfig;
325
- const ledger = yield* SubmissionLedger;
326
- const { ctx } = yield* DurableObjectContext;
327
- const existing = yield* ledger.lookup(SubmissionLookupByKey.make({
328
- threadId: identity.threadId,
329
- principal: request.principal,
330
- idempotencyKey: request.idempotencyKey
331
- }));
332
- if (Option.isSome(existing)) return;
333
- const inputBytes = utf8Bytes(request.inputPayload);
334
- if (inputBytes > config.limits.maxInputBytes) return yield* AdmissionLimitExceeded.make({
335
- limit: "input-bytes",
336
- actual: inputBytes,
337
- maximum: config.limits.maxInputBytes
338
- });
339
- const nonterminal = yield* Stream.runCollect(ledger.scanNonterminal);
340
- if (nonterminal.length >= config.limits.maxQueueDepthPerLane) return yield* AdmissionLimitExceeded.make({
341
- limit: "queue-depth",
342
- actual: nonterminal.length,
343
- maximum: config.limits.maxQueueDepthPerLane
344
- });
345
- const databaseBytes = yield* Effect.sync(() => ctx.storage.sql.databaseSize);
346
- if (databaseBytes > config.limits.maxDatabaseBytes) return yield* AdmissionLimitExceeded.make({
347
- limit: "database-bytes",
348
- actual: databaseBytes,
349
- maximum: config.limits.maxDatabaseBytes
350
- });
351
- });
352
- /**
353
- * The submit-capable projection of an Agent Binding on the OBJECT side: the input arrived
354
- * already encoded through the real input schema on the Worker side (`client.ts`), so the
355
- * Object admits the canonical `PersistedJson` payload as-is; the resolved Binding re-derives
356
- * everything else from the stored `(agentId, agentDigests)` at claim time (SUB-023).
357
- */
358
- const passthroughSubmitAgent = (agentId) => ({ definition: {
359
- id: agentId,
360
- input: PersistedJson
361
- } });
362
- const submitEndpoint = (encoded) => decodeSubmitRequest(encoded).pipe(Effect.mapError(protocolFailure("The submit request could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
363
- const identity = yield* ThreadObjectIdentity;
364
- const maintenance = yield* ThreadMaintenance;
365
- const runtime = yield* DurableAgentRuntime;
366
- yield* gateAdmissionLimits(request);
367
- const receipt = yield* maintenance.withMutation(runtime.submit(passthroughSubmitAgent(request.agentId), request.inputPayload, {
368
- threadId: identity.threadId,
369
- principal: request.principal,
370
- idempotencyKey: request.idempotencyKey,
371
- definitions: request.definitions
372
- }));
373
- return SubmitSucceeded.make({ receipt });
374
- })), respond, Effect.flatMap(encodeResponse));
375
- const awaitSettlementEndpoint = (encoded) => decodeReceipt(encoded).pipe(Effect.mapError(protocolFailure("The receipt could not be decoded")), Effect.flatMap((receipt) => Effect.gen(function* () {
376
- const settlement = yield* (yield* DurableAgentRuntime).awaitSettlement(receipt);
377
- return SettlementReached.make({ settlement });
378
- })), respond, Effect.flatMap(encodeResponse));
379
- const awaitProgressEndpoint = (encoded) => decodeAwaitProgressRequest(encoded).pipe(Effect.mapError(protocolFailure("The progress request could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
380
- const identity = yield* ThreadObjectIdentity;
381
- const runtime = yield* DurableAgentRuntime;
382
- const registry = yield* ProgressWaitRegistry;
383
- yield* Effect.scoped(Effect.gen(function* () {
384
- const cancelled = yield* registry.subscribe(request.waiterId);
385
- yield* Effect.raceFirst(runtime.awaitProgress(identity.threadId, request.afterSequence), cancelled);
386
- }));
387
- return ProgressObserved.make();
388
- })), respond, Effect.flatMap(encodeResponse));
389
- const cancelProgressEndpoint = (encoded) => decodeCancelProgressRequest(encoded).pipe(Effect.mapError(protocolFailure("The progress cancellation could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
390
- yield* (yield* ProgressWaitRegistry).cancel(request.waiterId);
391
- return ProgressCancelled.make();
392
- })), respond, Effect.flatMap(encodeResponse));
393
- const observePageEndpoint = (encoded) => decodeObservePageRequest(encoded).pipe(Effect.mapError(protocolFailure("The observe request could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
394
- const identity = yield* ThreadObjectIdentity;
395
- const store = yield* ThreadStore;
396
- yield* (yield* OperationAuthorizer).authorize(OperationAuthorizationRequest.make({
397
- operation: "observe",
398
- threadId: identity.threadId
399
- }));
400
- const records = yield* Stream.runCollect(store.read(ThreadRead.make({
401
- threadId: identity.threadId,
402
- ...request.afterSequence === void 0 ? {} : { afterSequence: request.afterSequence },
403
- limit: request.limit
404
- })));
405
- return ObservedPage.make({ records: [...records] });
406
- })), respond, Effect.flatMap(encodeResponse));
407
- const abortEndpoint = (encoded) => decodeAbortCommand(encoded).pipe(Effect.mapError(protocolFailure("The abort command could not be decoded")), Effect.flatMap((command) => Effect.gen(function* () {
408
- const maintenance = yield* ThreadMaintenance;
409
- const runtime = yield* DurableAgentRuntime;
410
- const intent = yield* maintenance.withMutation(runtime.abort(command));
411
- return AbortRecorded.make({ intent });
412
- })), respond, Effect.flatMap(encodeResponse));
413
- const resolveApprovalEndpoint = (encoded) => decodeApprovalDecisionCommand(encoded).pipe(Effect.mapError(protocolFailure("The approval command could not be decoded")), Effect.flatMap((command) => Effect.gen(function* () {
414
- const maintenance = yield* ThreadMaintenance;
415
- const runtime = yield* DurableAgentRuntime;
416
- const intent = yield* maintenance.withMutation(runtime.resolveApproval(command));
417
- return ApprovalRecorded.make({ intent });
418
- })), respond, Effect.flatMap(encodeResponse));
419
- const resolveUnknownEndpoint = (encoded) => decodeUnknownResolutionCommand(encoded).pipe(Effect.mapError(protocolFailure("The resolution command could not be decoded")), Effect.flatMap((command) => Effect.gen(function* () {
420
- const maintenance = yield* ThreadMaintenance;
421
- const runtime = yield* DurableAgentRuntime;
422
- const intent = yield* maintenance.withMutation(runtime.resolveUnknown(command));
423
- return UnknownResolutionRecorded.make({ intent });
424
- })), respond, Effect.flatMap(encodeResponse));
425
- /** Explain one Submission (`submissionId` present) or every nonterminal lane member. */
426
- var AdminExplainRequest = class extends Schema.Class("@effect-agent/platform-cloudflare/AdminExplainRequest")({ submissionId: Schema.optionalKey(SubmissionId) }) {};
427
- /** Verify carries no parameters — the addressed Object IS the lane. */
428
- var AdminVerifyRequest = class extends Schema.Class("@effect-agent/platform-cloudflare/AdminVerifyRequest")({}) {};
429
- /** Every typed failure of the four admin entry points, plus the protocol's own errors. */
430
- const AdminFailure = Schema.Union([
431
- OperationDenied,
432
- RetryRefused,
433
- LedgerError,
434
- RunJournalError,
435
- DigestError,
436
- OwnershipLost,
437
- SettlementConflict,
438
- ThreadStoreError,
439
- ThreadNotMaterialized,
440
- AppendConflict,
441
- FenceRejected,
442
- DurableRuntimeFailpointError,
443
- DurableAlarmError,
444
- HostProtocolError
445
- ]);
446
- var ExplainedRecovery = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/ExplainedRecovery")("ExplainedRecovery", { explanations: Schema.Array(RecoveryExplanation).check(Schema.isMaxLength(1024)) }) {};
447
- var VerifiedIntegrity = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/VerifiedIntegrity")("VerifiedIntegrity", { report: IntegrityReport }) {};
448
- var RetryExecuted = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/RetryExecuted")("RetryExecuted", { report: RecoveryReport }) {};
449
- var ObligationsScanned = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/ObligationsScanned")("ObligationsScanned", { report: ObligationReport }) {};
450
- /** The admin entry point failed TYPED on the Object; the failure re-decodes verbatim. */
451
- var AdminFailed = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/AdminFailed")("AdminFailed", { failure: AdminFailure }) {};
452
- /** The uniform answer of one admin entry point. Callers narrow by the tag their call implies. */
453
- const AdminResponse = Schema.Union([
454
- ExplainedRecovery,
455
- VerifiedIntegrity,
456
- RetryExecuted,
457
- ObligationsScanned,
458
- AdminFailed
459
- ]);
460
- const decodeAdminExplainRequest = Schema.decodeUnknownEffect(AdminExplainRequest);
461
- const decodeAdminVerifyRequest = Schema.decodeUnknownEffect(AdminVerifyRequest);
462
- const decodeRetryCommand = Schema.decodeUnknownEffect(RetryCommand);
463
- const decodeObligationThresholds = Schema.decodeUnknownEffect(ObligationThresholds);
464
- const encodeAdminResponse = Schema.encodeEffect(AdminResponse);
465
- const decodeAdminResponse = Schema.decodeUnknownEffect(AdminResponse);
466
- /** Fold one admin endpoint's typed failures into the uniform `AdminResponse` envelope. */
467
- const respondAdmin = (effect) => effect.pipe(Effect.map((result) => result), Effect.catch((failure) => Effect.succeed(AdminFailed.make({ failure }))));
468
- /** Encode the admin response envelope; an unencodable response degrades to a protocol failure. */
469
- const encodeAdminResponseTotal = (response) => encodeAdminResponse(response).pipe(Effect.catch((error) => Effect.succeed({
470
- _tag: "AdminFailed",
471
- failure: {
472
- _tag: "HostProtocolError",
473
- message: boundHostDiagnostic(`The admin response could not be encoded: ${error.message}`)
474
- }
475
- })));
476
- const explainEndpoint = (encoded) => decodeAdminExplainRequest(encoded).pipe(Effect.mapError(protocolFailure("The explain request could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
477
- const identity = yield* ThreadObjectIdentity;
478
- const runtime = yield* DurableAgentRuntime;
479
- const explanations = request.submissionId === void 0 ? yield* runtime.explainThread(identity.threadId) : [yield* runtime.explain(request.submissionId)];
480
- return ExplainedRecovery.make({ explanations });
481
- })), respondAdmin, Effect.flatMap(encodeAdminResponseTotal));
482
- const verifyEndpoint = (encoded) => decodeAdminVerifyRequest(encoded).pipe(Effect.mapError(protocolFailure("The verify request could not be decoded")), Effect.flatMap(() => Effect.gen(function* () {
483
- const identity = yield* ThreadObjectIdentity;
484
- const report = yield* (yield* DurableAgentRuntime).verify(identity.threadId);
485
- return VerifiedIntegrity.make({ report });
486
- })), respondAdmin, Effect.flatMap(encodeAdminResponseTotal));
487
- const retryEndpoint = (encoded) => decodeRetryCommand(encoded).pipe(Effect.mapError(protocolFailure("The retry command could not be decoded")), Effect.flatMap((command) => Effect.gen(function* () {
488
- const maintenance = yield* ThreadMaintenance;
489
- const runtime = yield* DurableAgentRuntime;
490
- const report = yield* maintenance.withMutation(runtime.retry(command));
491
- return RetryExecuted.make({ report });
492
- })), respondAdmin, Effect.flatMap(encodeAdminResponseTotal));
493
- const obligationsEndpoint = (encoded) => decodeObligationThresholds(encoded).pipe(Effect.mapError(protocolFailure("The obligation thresholds could not be decoded")), Effect.flatMap((thresholds) => Effect.gen(function* () {
494
- const report = yield* (yield* DurableAgentRuntime).scanObligations(thresholds);
495
- return ObligationsScanned.make({ report });
496
- })), respondAdmin, Effect.flatMap(encodeAdminResponseTotal));
497
- /**
498
- * Owner-side `portCall`: wrap a mutating envelope in the same pre-armed generation protocol as
499
- * public RPC (a routed mutation committed by THIS Object must already carry the alarm that will
500
- * finish it), execute on the LOCAL facets (never the routed decorators), then arm an immediate
501
- * alarm so the mutated lane is processed promptly. Protocol anomalies answer
502
- * `PortFailed(PortProtocolError)`.
503
- */
504
- const portCallEndpoint = (encoded) => Effect.gen(function* () {
505
- const ports = yield* ThreadObjectPorts;
506
- const maintenance = yield* ThreadMaintenance;
507
- const alarm = yield* DurableAlarmService;
508
- const decoded = yield* decodePortRequest(encoded).pipe(Effect.map((request) => ({
509
- _tag: "success",
510
- request
511
- })), Effect.catch((error) => Effect.succeed({
512
- _tag: "failure",
513
- message: error.message
514
- })));
515
- if (decoded._tag === "failure") return encodedPortProtocolFailure(`The port request could not be decoded: ${decoded.message}`);
516
- const mutating = isMutatingPortRequest(decoded.request);
517
- const handled = yield* (mutating ? maintenance.withMutation(ports.handle(decoded.request)) : ports.handle(decoded.request)).pipe(Effect.exit);
518
- if (handled._tag === "Failure") return encodedPortProtocolFailure("The owner Object could not arm its maintenance alarm before the mutation.");
519
- const response = yield* encodePortResponse(handled.value).pipe(Effect.catch((error) => Effect.succeed(encodedPortProtocolFailure(`The port response could not be encoded: ${error.message}`))));
520
- if (mutating) yield* alarm.scheduleNow.pipe(Effect.catch((error) => Effect.logWarning("ThreadObject.portCall: immediate re-arm failed", error)));
521
- return response;
522
- });
523
- const wakeEndpoint = Effect.gen(function* () {
524
- const identity = yield* ThreadObjectIdentity;
525
- yield* (yield* WakeScheduler).notify(identity.threadId);
526
- });
527
- const alarmEndpoint = Effect.gen(function* () {
528
- yield* (yield* ThreadMaintenance).pass;
529
- });
530
- const gateEndpoint = Effect.gen(function* () {
531
- yield* (yield* ThreadMaintenance).ensureAlarm;
532
- });
533
- /**
534
- * Adapter from effect-cf's native Durable Object services to Effect Agent's existing platform
535
- * ports. effect-cf owns the cached ManagedRuntime and supplies these values once per Object
536
- * incarnation; the durable runtime continues to depend only on the narrow services below.
537
- */
538
- const effectCfPlatformLayer = (namespaceBinding, rpcTracing = false) => {
539
- const context = Layer.effect(DurableObjectContext)(Effect.gen(function* () {
540
- const state = yield* DurableObjectState.DurableObjectState;
541
- const env = yield* WorkerEnvironment;
542
- return DurableObjectContext.of({
543
- ctx: state.raw,
544
- env
545
- });
546
- }));
547
- const namespace = Layer.effect(ThreadObjectNamespace)(Effect.gen(function* () {
548
- const env = yield* WorkerEnvironment;
549
- const binding = yield* threadNamespaceFromEnv(env, namespaceBinding);
550
- return ThreadObjectNamespace.of({
551
- namespace: binding,
552
- ...rpcTracing === true ? { rpcTracing: namespaceBinding } : {}
553
- });
554
- }));
555
- return Layer.merge(context, namespace);
556
- };
557
- /**
558
- * Export a composed application Layer as a native Durable Object class.
559
- * Bootstrap services are provided to the whole graph before it acquires, so application Layers
560
- * can yield effect-cf's WorkerEnvironment and DurableObjectState, derived identity, and Crypto.
561
- * Application dependencies remain visible until Layer.provide satisfies them. effect-cf owns the
562
- * cached ManagedRuntime, native RPC methods, event scopes, and telemetry flushing.
563
- * Initialization is local and bounded inside the constructor gate. Cloudflare eviction does not
564
- * guarantee finalizers; put resources requiring timely release in scoped operations or eventLayer.
565
- */
566
- const make = (applicationLayer, options) => {
567
- const application = applicationLayer.pipe(Layer.provideMerge(layerConfig(options)), Layer.provideMerge(effectCfPlatformLayer(options.namespaceBinding, options.rpcTracing)));
568
- const runtime = Layer.effectContext(Effect.gen(function* () {
569
- const state = yield* DurableObjectState.DurableObjectState;
570
- const scope = yield* Effect.scope;
571
- return yield* state.blockConcurrencyWhile(Effect.gen(function* () {
572
- const services = yield* Layer.buildWithScope(application, scope);
573
- yield* gateEndpoint.pipe(Effect.provide(services));
574
- return services;
575
- }));
576
- }));
577
- const rpc = {
578
- submitEncoded: (encoded) => submitEndpoint(encoded),
579
- awaitSettlementEncoded: (encoded) => awaitSettlementEndpoint(encoded),
580
- awaitProgressEncoded: (encoded) => awaitProgressEndpoint(encoded),
581
- cancelProgressEncoded: (encoded) => cancelProgressEndpoint(encoded),
582
- observePage: (encoded) => observePageEndpoint(encoded),
583
- abortEncoded: (encoded) => abortEndpoint(encoded),
584
- resolveApprovalEncoded: (encoded) => resolveApprovalEndpoint(encoded),
585
- resolveUnknownEncoded: (encoded) => resolveUnknownEndpoint(encoded),
586
- explainEncoded: (encoded) => explainEndpoint(encoded),
587
- verifyEncoded: (encoded) => verifyEndpoint(encoded),
588
- retryEncoded: (encoded) => retryEndpoint(encoded),
589
- obligationsEncoded: (encoded) => obligationsEndpoint(encoded),
590
- portCall: (encoded) => portCallEndpoint(encoded),
591
- wake: () => wakeEndpoint
592
- };
593
- const EffectCfThreadObject = DurableObject.make(runtime, {
594
- ...options.rpcTracing === true ? { rpcTracing: { service: options.namespaceBinding } } : {},
595
- ...options.eventLayer === void 0 ? {} : { eventLayer: options.eventLayer },
596
- initialize: Effect.void,
597
- rpc,
598
- alarm: () => alarmEndpoint
599
- });
600
- class ThreadObject extends EffectCfThreadObject {
601
- alarm(alarmInfo) {
602
- return super.alarm?.(alarmInfo);
603
- }
604
- }
605
- return ThreadObject;
606
- };
607
- //#endregion
608
- //#region src/code-mode-executor.ts
609
- /**
610
- * The Cloudflare Dynamic Worker `CodeExecutor` adapter (C4 of ADR-0017;
611
- * DEPLOY-011). Each pass loads one fresh Worker through the Worker Loader
612
- * with `globalOutbound: null`, so generated code has no ambient network,
613
- * bindings, or secrets; its only authority is the pass-scoped RPC target that
614
- * routes back into the owning event's `CodeExecutionHost` service. Platform
615
- * CPU limits stop synchronous runaway programs; the executor-owned wall-clock
616
- * deadline interrupts asynchronously suspended passes. Deployment class `E`
617
- * only: the adapter records no persistent state and a later pass may run in a
618
- * completely different isolate.
619
- */
620
- const dynamicWorkerImplementation = SandboxImplementation.make({
621
- isolation: "isolated",
622
- identity: "cloudflare-dynamic-worker"
623
- });
624
- /**
625
- * One object-capability endpoint for one execution pass. Workers RPC invokes
626
- * the target in the request context where it was created, so the native
627
- * Promise returned by `dispatch` and the Effect fiber that settles it share
628
- * one I/O owner. Passing the target as `run()`'s argument also scopes the
629
- * remote stub to that RPC call; no request state lives at module scope.
630
- */
631
- var CodeModePassHostTarget = class extends RpcTarget {
632
- #dispatch;
633
- constructor(dispatch) {
634
- super();
635
- this.#dispatch = dispatch;
636
- }
637
- call(hostCall) {
638
- return this.#dispatch(hostCall);
639
- }
640
- };
641
- /**
642
- * The fixed harness loaded as the dynamic worker's main module. The generated
643
- * source becomes `program.mjs` (`export default (<expression>);`) — a module,
644
- * never `eval`. The harness installs namespace globals and a bounded console,
645
- * imports the program, invokes it exactly once, and returns one envelope the
646
- * host validates through Effect Schema.
647
- */
648
- const HARNESS_MODULE = String.raw`
649
- import { WorkerEntrypoint } from "cloudflare:workers";
650
- import programDefault from "./program.js";
651
-
652
- const encoder = new TextEncoder();
653
- const utf8 = (text) => encoder.encode(text).byteLength;
654
- const safeText = (value) => {
655
- try {
656
- if (value instanceof Error) return (value.name + ": " + value.message).slice(0, 4000);
657
- if (typeof value === "string") return value.slice(0, 4000);
658
- const encoded = JSON.stringify(value);
659
- return (encoded === undefined ? String(value) : encoded).slice(0, 4000);
660
- } catch {
661
- return "[unserializable value]";
662
- }
663
- };
664
- const safeJson = (value) => {
665
- try {
666
- const encoded = JSON.stringify(value);
667
- if (encoded !== undefined && encoded.length <= 4000) return JSON.parse(encoded);
668
- } catch {}
669
- return safeText(value);
670
- };
671
-
672
- export default class CodeModeHarness extends WorkerEntrypoint {
673
- async run(host) {
674
- const config = this.env.CODE_MODE_PASS;
675
- const limits = config.limits;
676
- const logs = [];
677
- let logBytes = 0;
678
- let fatal;
679
- const boundedLogs = () => logs.slice(0, 4096);
680
- const write = (...values) => {
681
- const joined = values.map(safeText).join(" ");
682
- const line = joined.length > 16000 ? joined.slice(0, 15999) + "…" : joined;
683
- const bytes = utf8(line);
684
- if (logs.length >= 4096 || logBytes + bytes > limits.maxLogBytes) {
685
- fatal = fatal ?? { _tag: "log-limit", observed: logBytes + bytes, logs: boundedLogs() };
686
- throw new Error("code-mode log limit exceeded");
687
- }
688
- logs.push(line);
689
- logBytes += bytes;
690
- };
691
- globalThis.console = { log: write, info: write, warn: write, error: write, debug: write };
692
-
693
- let hostCalls = 0;
694
- const makeMethod = (namespace, method) => async (argument) => {
695
- hostCalls += 1;
696
- if (hostCalls > limits.maxHostCalls) {
697
- fatal = fatal ?? { _tag: "host-call-limit", logs: boundedLogs() };
698
- throw new Error("code-mode host-call limit exceeded");
699
- }
700
- let argText;
701
- try {
702
- argText = JSON.stringify(argument);
703
- } catch {}
704
- if (argText === undefined || utf8(argText) > limits.maxHostCallArgumentBytes) {
705
- fatal = fatal ?? {
706
- _tag: "argument-limit",
707
- observed: argText === undefined ? 0 : utf8(argText),
708
- logs: boundedLogs(),
709
- };
710
- throw new Error("code-mode host-call argument limit exceeded");
711
- }
712
- const outcome = await host.call({
713
- namespace,
714
- method,
715
- argument: JSON.parse(argText),
716
- });
717
- if (outcome !== null && typeof outcome === "object" && outcome._tag === "CodeHostCallSuccess") {
718
- return outcome.value;
719
- }
720
- if (outcome !== null && typeof outcome === "object" && outcome._tag === "CodeHostCallFailure") {
721
- throw outcome.error;
722
- }
723
- fatal = fatal ?? { _tag: "protocol", message: "host returned an unrecognized outcome" };
724
- throw new Error("code-mode host protocol violation");
725
- };
726
- for (const namespace of config.namespaces) {
727
- const methods = {};
728
- for (const method of namespace.methods) {
729
- methods[method] = makeMethod(namespace.name, method);
730
- }
731
- globalThis[namespace.name] = methods;
732
- }
733
-
734
- // program.js is imported statically at the top of this module, so a
735
- // syntactically invalid program fails the whole harness at load (mapped
736
- // to a source error by the host). Using a static import keeps this module
737
- // free of dynamic-import expressions, which single-script Miniflare hosts
738
- // reject. The isolation boundary does NOT depend on the ordering of this
739
- // import versus the console/namespace shims installed below: the loaded
740
- // Worker has globalOutbound: null and no bindings, secrets, or env from
741
- // the Worker Loader config BEFORE any module in the graph evaluates, so
742
- // module-level program code has no ambient authority regardless. The
743
- // shims below are usability wrappers (bounded console, namespace globals),
744
- // and the accepted program is a single async-function expression whose
745
- // body runs only when invoked here — after the shims exist.
746
- const program = programDefault;
747
- if (typeof program !== "function") {
748
- return { _tag: "source-not-a-function", actual: typeof program };
749
- }
750
- try {
751
- const value = await program();
752
- if (fatal !== undefined) return fatal;
753
- let text;
754
- try {
755
- text = JSON.stringify(value);
756
- } catch {}
757
- if (text === undefined) {
758
- return {
759
- _tag: "program-failed",
760
- reason: "non-json-result",
761
- thrown: null,
762
- message: "The program must return a JSON value",
763
- logs: boundedLogs(),
764
- };
765
- }
766
- const resultBytes = utf8(text);
767
- if (resultBytes > limits.maxResultBytes) {
768
- return { _tag: "result-limit", observed: resultBytes, logs: boundedLogs() };
769
- }
770
- return {
771
- _tag: "completed",
772
- value: JSON.parse(text),
773
- logs: boundedLogs(),
774
- hostCalls,
775
- logBytes,
776
- resultBytes,
777
- };
778
- } catch (cause) {
779
- if (fatal !== undefined) return fatal;
780
- return {
781
- _tag: "program-failed",
782
- reason: cause instanceof Error ? "threw" : "rejected",
783
- thrown: safeJson(cause),
784
- message: safeText(cause),
785
- logs: boundedLogs(),
786
- };
787
- }
788
- }
789
- }
790
- `;
791
- const BoundedLogs = Schema.Array(Schema.String.check(Schema.isMaxLength(16384))).check(Schema.isMaxLength(4096));
792
- const HarnessCompleted = Schema.TaggedStruct("completed", {
793
- value: Schema.Json,
794
- logs: BoundedLogs,
795
- hostCalls: Schema.Natural,
796
- logBytes: Schema.Natural,
797
- resultBytes: Schema.Natural
798
- });
799
- const HarnessSourceInvalid = Schema.TaggedStruct("source-invalid", { message: Schema.String });
800
- const HarnessNotAFunction = Schema.TaggedStruct("source-not-a-function", { actual: Schema.String });
801
- const HarnessProgramFailed = Schema.TaggedStruct("program-failed", {
802
- reason: Schema.Literals([
803
- "threw",
804
- "rejected",
805
- "non-json-result"
806
- ]),
807
- thrown: Schema.Json,
808
- message: Schema.String,
809
- logs: BoundedLogs
810
- });
811
- const HarnessLogLimit = Schema.TaggedStruct("log-limit", {
812
- observed: Schema.Natural,
813
- logs: BoundedLogs
814
- });
815
- const HarnessArgumentLimit = Schema.TaggedStruct("argument-limit", {
816
- observed: Schema.Natural,
817
- logs: BoundedLogs
818
- });
819
- const HarnessResultLimit = Schema.TaggedStruct("result-limit", {
820
- observed: Schema.Natural,
821
- logs: BoundedLogs
822
- });
823
- const HarnessHostCallLimit = Schema.TaggedStruct("host-call-limit", { logs: BoundedLogs });
824
- const HarnessProtocol = Schema.TaggedStruct("protocol", { message: Schema.String });
825
- const HarnessOutcome = Schema.Union([
826
- HarnessCompleted,
827
- HarnessSourceInvalid,
828
- HarnessNotAFunction,
829
- HarnessProgramFailed,
830
- HarnessLogLimit,
831
- HarnessArgumentLimit,
832
- HarnessResultLimit,
833
- HarnessHostCallLimit,
834
- HarnessProtocol
835
- ]);
836
- const HarnessPassConfig = Schema.Struct({
837
- namespaces: Schema.Array(Schema.Struct({
838
- name: Schema.NonEmptyString,
839
- methods: Schema.Array(Schema.NonEmptyString).check(Schema.isMaxLength(64))
840
- })).check(Schema.isMaxLength(32)),
841
- limits: Schema.Struct({
842
- maxLogBytes: Schema.Natural,
843
- maxResultBytes: Schema.Natural,
844
- maxHostCalls: Schema.Natural,
845
- maxHostCallArgumentBytes: Schema.Natural
846
- })
847
- });
848
- const encodeHarnessPassConfig = Schema.encodeSync(HarnessPassConfig);
849
- const encodeJsonPayload = Schema.encodeSync(Schema.fromJsonString(Schema.Json));
850
- const decodeJsonPayload = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json));
851
- const decodeHarnessOutcome = (value) => {
852
- try {
853
- return Schema.decodeUnknownOption(HarnessOutcome)(value);
854
- } catch {
855
- return Option.none();
856
- }
857
- };
858
- const decodeHostCall = (value) => {
859
- try {
860
- return Schema.decodeUnknownOption(CodeHostCall)(value);
861
- } catch {
862
- return Option.none();
863
- }
864
- };
865
- const decodeHostCallResult = (value) => {
866
- try {
867
- return Schema.decodeUnknownOption(CodeHostCallResult)(value);
868
- } catch {
869
- return Option.none();
870
- }
871
- };
872
- /** Dispose a Cloudflare RPC handle when the runtime supplies its untyped disposal hook. @internal */
873
- const disposeRpcHandle = (handle) => Effect.try({
874
- try: () => {
875
- if (typeof handle !== "object" && typeof handle !== "function" || handle === null) return;
876
- if (!(Symbol.dispose in handle)) return;
877
- const dispose = Reflect.get(handle, Symbol.dispose);
878
- if (typeof dispose === "function") Reflect.apply(dispose, handle, []);
879
- },
880
- catch: (cause) => safeCauseDiagnostic(cause, "The Cloudflare RPC disposal hook failed without a diagnostic")
881
- }).pipe(Effect.catch((diagnostic) => Effect.logWarning(`Cloudflare RPC handle disposal failed: ${diagnostic}`).pipe(Effect.ignoreCause)));
882
- const encodeHostResultPayload = (outcome) => {
883
- try {
884
- const payload = outcome._tag === "CodeHostCallSuccess" ? outcome.value : outcome.error;
885
- const encodedPayload = encodeJsonPayload(payload);
886
- return {
887
- encodedPayload,
888
- resultBytes: utf8ByteLength(encodedPayload)
889
- };
890
- } catch {
891
- return;
892
- }
893
- };
894
- const utf8ByteLength = (value) => {
895
- let total = 0;
896
- for (const character of value) {
897
- const codePoint = character.codePointAt(0) ?? 0;
898
- total += codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
899
- }
900
- return total;
901
- };
902
- /** Reserved global names the harness owns inside the dynamic worker. */
903
- const reservedHarnessGlobals = /* @__PURE__ */ new Set(["console"]);
904
- const makeExecute = (options, clock) => Effect.fn("DynamicWorkerCodeExecutor.execute")(function* (request) {
905
- if (request.network._tag !== "NetworkDisabled") return yield* CodeExecutorUnsupportedError.make({
906
- implementation: dynamicWorkerImplementation,
907
- feature: "network",
908
- message: "The Dynamic Worker executor denies all egress with globalOutbound: null; an allowlist is not supported in the first slice"
909
- });
910
- const sourceBytes = utf8ByteLength(request.source);
911
- if (sourceBytes > request.limits.maxSourceBytes) return yield* CodeSourceError.make({
912
- implementation: dynamicWorkerImplementation,
913
- reason: "oversized",
914
- message: `Source is ${sourceBytes} bytes; the request allows ${request.limits.maxSourceBytes}`
915
- });
916
- for (const namespace of request.namespaces) if (reservedHarnessGlobals.has(namespace.name)) return yield* CodeExecutorUnsupportedError.make({
917
- implementation: dynamicWorkerImplementation,
918
- feature: "namespaces",
919
- message: `Namespace ${namespace.name} collides with a harness binding`
920
- });
921
- const host = yield* CodeExecutionHost;
922
- const startedAt = clock.monotonicTimeNanosUnsafe();
923
- const passDeadline = startedAt + Duration.toNanosUnsafe(request.limits.maxWallTime);
924
- const remainingPassWallTime = () => {
925
- const now = clock.monotonicTimeNanosUnsafe();
926
- return Duration.nanos(passDeadline > now ? passDeadline - now : 0n);
927
- };
928
- let issuedHostCalls = 0;
929
- let passOpen = true;
930
- const queuedHostCalls = [];
931
- let passFailure;
932
- const failPass = (error) => {
933
- if (passFailure === void 0) passFailure = error;
934
- };
935
- const rejectQueuedHostCalls = (reason) => {
936
- for (const queued of queuedHostCalls.splice(0)) queued.reject(reason);
937
- };
938
- const queue = yield* Queue.unbounded();
939
- const deliverHostOutcome = (queued, outcome) => Effect.gen(function* () {
940
- const decoded = decodeHostCallResult(outcome);
941
- if (Option.isNone(decoded)) {
942
- const error = CodeExecutionProtocolError.make({
943
- implementation: dynamicWorkerImplementation,
944
- message: "The execution host returned a value outside the CodeHostCallResult schema"
945
- });
946
- failPass(error);
947
- return yield* error;
948
- }
949
- const encoded = encodeHostResultPayload(decoded.value);
950
- if (encoded === void 0 || encoded.resultBytes > request.limits.maxHostCallResultBytes) {
951
- const error = CodeOutputLimitError.make({
952
- implementation: dynamicWorkerImplementation,
953
- surface: "host-call-result",
954
- limit: request.limits.maxHostCallResultBytes,
955
- observed: encoded?.resultBytes ?? 0,
956
- logs: []
957
- });
958
- failPass(error);
959
- return yield* error;
960
- }
961
- const normalizedPayload = decodeJsonPayload(encoded.encodedPayload);
962
- if (Option.isNone(normalizedPayload)) {
963
- const error = CodeExecutionProtocolError.make({
964
- implementation: dynamicWorkerImplementation,
965
- message: "The execution host returned a result that could not cross the JSON boundary"
966
- });
967
- failPass(error);
968
- return yield* error;
969
- }
970
- queued.resolve(decoded.value._tag === "CodeHostCallSuccess" ? {
971
- _tag: "CodeHostCallSuccess",
972
- value: normalizedPayload.value
973
- } : {
974
- _tag: "CodeHostCallFailure",
975
- error: normalizedPayload.value
976
- });
977
- });
978
- const server = yield* Effect.gen(function* () {
979
- while (true) {
980
- const work = yield* Queue.take(queue);
981
- if (work._tag === "limit") {
982
- const error = CodeHostCallLimitError.make({
983
- implementation: dynamicWorkerImplementation,
984
- limit: request.limits.maxHostCalls,
985
- logs: []
986
- });
987
- failPass(error);
988
- return yield* error;
989
- }
990
- const queued = work.queued;
991
- yield* host.call(queued.call).pipe(Effect.timeoutOrElse({
992
- duration: remainingPassWallTime(),
993
- orElse: () => {
994
- const error = CodeExecutionTimeoutError.make({
995
- implementation: dynamicWorkerImplementation,
996
- kind: "wall-clock",
997
- maxWallTime: request.limits.maxWallTime,
998
- logs: []
999
- });
1000
- failPass(error);
1001
- return error;
1002
- }
1003
- }), Effect.flatMap((outcome) => deliverHostOutcome(queued, outcome)), Effect.tapError(() => Effect.sync(() => queued.reject(/* @__PURE__ */ new Error("Code Mode host call failed")))), Effect.onInterrupt(() => Effect.sync(() => queued.reject(/* @__PURE__ */ new Error("Code Mode pass is closing")))));
1004
- }
1005
- }).pipe(Effect.forkScoped);
1006
- const dispatch = (hostCall) => {
1007
- if (!passOpen) return Promise.reject(/* @__PURE__ */ new Error("Code Mode pass is closing"));
1008
- issuedHostCalls += 1;
1009
- if (issuedHostCalls > request.limits.maxHostCalls) {
1010
- const error = CodeHostCallLimitError.make({
1011
- implementation: dynamicWorkerImplementation,
1012
- limit: request.limits.maxHostCalls,
1013
- logs: []
1014
- });
1015
- failPass(error);
1016
- Queue.offerUnsafe(queue, { _tag: "limit" });
1017
- return Promise.reject(/* @__PURE__ */ new Error("host-call limit exceeded"));
1018
- }
1019
- const decoded = decodeHostCall(hostCall);
1020
- if (Option.isNone(decoded)) return Promise.reject(/* @__PURE__ */ new TypeError("host calls must match the CodeHostCall schema"));
1021
- return new Promise((resolve, reject) => {
1022
- const queued = {
1023
- call: decoded.value,
1024
- resolve,
1025
- reject
1026
- };
1027
- queuedHostCalls.push(queued);
1028
- Queue.offerUnsafe(queue, {
1029
- _tag: "call",
1030
- queued
1031
- });
1032
- });
1033
- };
1034
- const closeAdmission = Effect.sync(() => {
1035
- passOpen = false;
1036
- rejectQueuedHostCalls(/* @__PURE__ */ new Error("Code Mode pass is closing"));
1037
- });
1038
- yield* Effect.addFinalizer(() => closeAdmission.pipe(Effect.andThen(Fiber.interrupt(server))));
1039
- const workerCode = {
1040
- compatibilityDate: options.compatibilityDate ?? "2025-05-01",
1041
- mainModule: "harness.js",
1042
- modules: {
1043
- "harness.js": HARNESS_MODULE,
1044
- "program.js": `export default (\n${request.source}\n);`
1045
- },
1046
- env: { CODE_MODE_PASS: encodeHarnessPassConfig({
1047
- namespaces: request.namespaces.map((namespace) => ({
1048
- name: namespace.name,
1049
- methods: namespace.methods
1050
- })),
1051
- limits: {
1052
- maxLogBytes: request.limits.maxLogBytes,
1053
- maxResultBytes: request.limits.maxResultBytes,
1054
- maxHostCalls: request.limits.maxHostCalls,
1055
- maxHostCallArgumentBytes: request.limits.maxHostCallArgumentBytes
1056
- }
1057
- }) },
1058
- globalOutbound: null,
1059
- ...request.limits.cpuMillis === void 0 ? {} : { limits: {
1060
- cpuMs: request.limits.cpuMillis,
1061
- subRequests: request.limits.maxHostCalls + 8
1062
- } }
1063
- };
1064
- const worker = yield* Effect.acquireRelease(Effect.try({
1065
- try: () => options.loader.load(workerCode),
1066
- catch: (cause) => {
1067
- const text = safeCauseMessage(cause, "The Worker Loader failed without a diagnostic");
1068
- if (/syntaxerror|failed to (compile|parse)/i.test(text)) return CodeSourceError.make({
1069
- implementation: dynamicWorkerImplementation,
1070
- reason: "invalid",
1071
- message: text.slice(0, 8e3)
1072
- });
1073
- return CodeExecutorStartError.make({
1074
- implementation: dynamicWorkerImplementation,
1075
- message: `The Worker Loader rejected the pass: ${text}`.slice(0, 8e3),
1076
- cause
1077
- });
1078
- }
1079
- }), disposeRpcHandle);
1080
- const entrypoint = yield* Effect.acquireRelease(Effect.try({
1081
- try: () => worker.getEntrypoint(),
1082
- catch: (cause) => classifyWorkerFailure(cause, request.limits.maxWallTime)
1083
- }), disposeRpcHandle);
1084
- const rpc = Effect.tryPromise({
1085
- try: () => entrypoint.run(new CodeModePassHostTarget(dispatch)),
1086
- catch: (cause) => classifyWorkerFailure(cause, request.limits.maxWallTime)
1087
- });
1088
- const exit = yield* Effect.raceFirst(rpc.pipe(Effect.timeoutOrElse({
1089
- duration: remainingPassWallTime(),
1090
- orElse: () => CodeExecutionTimeoutError.make({
1091
- implementation: dynamicWorkerImplementation,
1092
- kind: "wall-clock",
1093
- maxWallTime: request.limits.maxWallTime,
1094
- logs: []
1095
- })
1096
- })), Fiber.join(server)).pipe(Effect.exit);
1097
- yield* closeAdmission;
1098
- yield* Fiber.interrupt(server);
1099
- if (passFailure !== void 0) return yield* passFailure;
1100
- if (Exit.isFailure(exit)) return yield* Effect.failCause(exit.cause);
1101
- const raw = exit.value;
1102
- const finishedAt = clock.monotonicTimeNanosUnsafe();
1103
- const outcome = decodeHarnessOutcome(raw);
1104
- if (Option.isNone(outcome)) return yield* CodeExecutionProtocolError.make({
1105
- implementation: dynamicWorkerImplementation,
1106
- message: "The dynamic worker returned a value outside the harness envelope schema"
1107
- });
1108
- switch (outcome.value._tag) {
1109
- case "completed": return CodeExecutionResult.make({
1110
- implementation: dynamicWorkerImplementation,
1111
- value: outcome.value.value,
1112
- logs: outcome.value.logs,
1113
- resourceUse: CodeExecutionResourceUse.make({
1114
- wallTime: Duration.nanos(finishedAt > startedAt ? finishedAt - startedAt : 0n),
1115
- hostCalls: outcome.value.hostCalls,
1116
- logBytes: outcome.value.logBytes,
1117
- resultBytes: outcome.value.resultBytes
1118
- })
1119
- });
1120
- case "source-invalid": return yield* CodeSourceError.make({
1121
- implementation: dynamicWorkerImplementation,
1122
- reason: "invalid",
1123
- message: outcome.value.message.slice(0, 8e3)
1124
- });
1125
- case "source-not-a-function": return yield* CodeSourceError.make({
1126
- implementation: dynamicWorkerImplementation,
1127
- reason: "not-a-function",
1128
- message: `The source expression evaluated to ${outcome.value.actual}; it must evaluate to one async function`
1129
- });
1130
- case "program-failed": return yield* CodeProgramFailedError.make({
1131
- implementation: dynamicWorkerImplementation,
1132
- reason: outcome.value.reason,
1133
- thrown: outcome.value.thrown,
1134
- message: outcome.value.message.slice(0, 8e3),
1135
- logs: outcome.value.logs
1136
- });
1137
- case "log-limit": return yield* CodeOutputLimitError.make({
1138
- implementation: dynamicWorkerImplementation,
1139
- surface: "logs",
1140
- limit: request.limits.maxLogBytes,
1141
- observed: outcome.value.observed,
1142
- logs: outcome.value.logs
1143
- });
1144
- case "argument-limit": return yield* CodeOutputLimitError.make({
1145
- implementation: dynamicWorkerImplementation,
1146
- surface: "host-call-argument",
1147
- limit: request.limits.maxHostCallArgumentBytes,
1148
- observed: outcome.value.observed,
1149
- logs: outcome.value.logs
1150
- });
1151
- case "result-limit": return yield* CodeOutputLimitError.make({
1152
- implementation: dynamicWorkerImplementation,
1153
- surface: "result",
1154
- limit: request.limits.maxResultBytes,
1155
- observed: outcome.value.observed,
1156
- logs: outcome.value.logs
1157
- });
1158
- case "host-call-limit": return yield* CodeHostCallLimitError.make({
1159
- implementation: dynamicWorkerImplementation,
1160
- limit: request.limits.maxHostCalls,
1161
- logs: outcome.value.logs
1162
- });
1163
- case "protocol": return yield* CodeExecutionProtocolError.make({
1164
- implementation: dynamicWorkerImplementation,
1165
- message: outcome.value.message.slice(0, 8e3)
1166
- });
1167
- }
1168
- });
1169
- /**
1170
- * Expected worker-level failures map into the typed union with bounded
1171
- * diagnostics; anything unrecognized stays a start/termination error rather
1172
- * than a fabricated program result.
1173
- */
1174
- const classifyWorkerFailure = (cause, maxWallTime) => {
1175
- const text = safeCauseDiagnostic(cause, "[unserializable worker failure]");
1176
- if (/syntaxerror|failed to (compile|parse)/i.test(text)) return CodeSourceError.make({
1177
- implementation: dynamicWorkerImplementation,
1178
- reason: "invalid",
1179
- message: text.slice(0, 8e3)
1180
- });
1181
- if (/cpu/i.test(text)) return CodeExecutionTimeoutError.make({
1182
- implementation: dynamicWorkerImplementation,
1183
- kind: "cpu",
1184
- maxWallTime,
1185
- logs: []
1186
- });
1187
- if (/failed to start worker/i.test(text)) return CodeExecutorStartError.make({
1188
- implementation: dynamicWorkerImplementation,
1189
- message: text.slice(0, 8e3),
1190
- cause
1191
- });
1192
- return CodeExecutorTerminatedError.make({
1193
- implementation: dynamicWorkerImplementation,
1194
- message: text.slice(0, 8e3)
1195
- });
1196
- };
1197
- /** Layer building the Dynamic Worker `CodeExecutor` from resolved bindings. */
1198
- const dynamicWorkerCodeExecutorLayer = (options) => Layer.effect(CodeExecutor, Effect.gen(function* () {
1199
- const clock = yield* Clock.Clock;
1200
- return CodeExecutor.of({ execute: makeExecute(options, clock) });
1201
- }));
1202
- /** Assemble Code Mode handlers with the isolated Dynamic Worker executor. */
1203
- const CloudflareCodeMode = {
1204
- /**
1205
- * Provide the selected tool handlers at construction, where Code Mode captures them.
1206
- * Their errors and remaining dependencies stay visible. The definition still owns the
1207
- * allowlist and limits; the runtime supplies the live Tool broker for each scoped pass.
1208
- */
1209
- layer: (definition, options) => definition.handlers.pipe(Layer.provide(options.handlers), Layer.provide(dynamicWorkerCodeExecutorLayer(options))) };
1210
- //#endregion
1211
- //#region src/memory.ts
1212
- var MemoryObjectNamespace = class extends Context.Service()("@effect-agent/platform-cloudflare/MemoryObjectNamespace") {};
1213
- /** Namespace version and identity are already canonicalized by MemoryNamespace. */
1214
- const memoryObjectName = (namespace) => namespace.address;
1215
- /**
1216
- * Effect-native, host-bound memory client. Recall revalidates the entire admitted lookup
1217
- * in one RPC and renders it locally. No retries or per-source splitting occur here.
1218
- * Interrupted callers stop waiting; the owner has its own deadline. A timed-out write
1219
- * may have committed: reconcile by sending the identical operation ID and command.
1220
- */
1221
- const makeMemoryClient = Effect.fn("CloudflareMemoryClient.make")(function* (access, principal, rpcLimits = defaultMemoryRpcLimits) {
1222
- const validated = yield* Schema.decodeUnknownEffect(MemoryRpcLimits)(rpcLimits).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })));
1223
- const bound = yield* Schema.decodeUnknownEffect(MemoryAccess.Wire)(access).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })));
1224
- principal = yield* Schema.decodeUnknownEffect(Principal)(principal).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })));
1225
- const { namespace } = yield* MemoryObjectNamespace;
1226
- const call = Effect.fn("CloudflareMemoryClient.call")(function* (request) {
1227
- const decoded = yield* Schema.decodeUnknownEffect(MemoryOwnerRequest)(request).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })));
1228
- const encoded = yield* encodeMemoryWire(MemoryOwnerRequest, decoded, validated.maxRequestBytes);
1229
- const raw = yield* Effect.tryPromise({
1230
- try: () => namespace.get(namespace.idFromName(memoryObjectName(bound.namespace))).memory(encoded),
1231
- catch: () => MemoryRpcError.make({ reason: "unavailable" })
1232
- });
1233
- const response = yield* decodeMemoryWire(MemoryOwnerResponse, raw, validated.maxResponseBytes);
1234
- if (response._tag === "Failed") return yield* response.failure;
1235
- if (!MemoryNamespace.equals(response.access.namespace, bound.namespace) || response.access.scope !== bound.scope) return yield* MemoryRpcError.make({ reason: "protocol" });
1236
- return response;
1237
- });
1238
- const withinDeadline = (effect, timeoutMillis) => effect.pipe(Effect.timeoutOrElse({
1239
- duration: timeoutMillis,
1240
- orElse: () => Effect.fail(MemoryRpcError.make({ reason: "timeout" }))
1241
- }));
1242
- const revalidate = Effect.fn("CloudflareMemoryClient.revalidate")(function* (lookup, limits) {
1243
- limits = yield* Schema.decodeUnknownEffect(MemoryRecallLimits)(limits).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })));
1244
- const timeoutMillis = Math.min(validated.timeoutMillis, limits.timeoutMillis);
1245
- return yield* Effect.gen(function* () {
1246
- const response = yield* call({
1247
- _tag: "Revalidate",
1248
- version: 1,
1249
- access: bound,
1250
- principal,
1251
- lookup,
1252
- limits,
1253
- deadlineMillis: (yield* Clock.currentTimeMillis) + timeoutMillis
1254
- });
1255
- if (response._tag !== "Lookup") return yield* MemoryRpcError.make({ reason: "protocol" });
1256
- return response.lookup;
1257
- }).pipe((effect) => withinDeadline(effect, timeoutMillis));
1258
- });
1259
- const change = Effect.fn("CloudflareMemoryClient.change")(function* (write) {
1260
- if (!MemoryNamespace.equals(write.key.namespace, bound.namespace)) return yield* MemoryRpcError.make({ reason: "denied" });
1261
- return yield* Effect.gen(function* () {
1262
- const response = yield* call({
1263
- _tag: "Change",
1264
- version: 1,
1265
- access: bound,
1266
- principal,
1267
- write,
1268
- deadlineMillis: (yield* Clock.currentTimeMillis) + validated.timeoutMillis
1269
- });
1270
- if (response._tag !== "Changed" || response.document.key.id !== write.key.id) return yield* MemoryRpcError.make({ reason: "protocol" });
1271
- return yield* MemoryDocument.restore(access.namespace, response.document);
1272
- }).pipe((effect) => withinDeadline(effect, validated.timeoutMillis));
1273
- });
1274
- const revalidateSemantic = Effect.fn("CloudflareMemoryClient.revalidateSemantic")(function* (found, profile, limits) {
1275
- return yield* Effect.gen(function* () {
1276
- const response = yield* call({
1277
- _tag: "RevalidateSemantic",
1278
- version: 1,
1279
- access: bound,
1280
- principal,
1281
- found,
1282
- profile,
1283
- limits,
1284
- deadlineMillis: (yield* Clock.currentTimeMillis) + validated.timeoutMillis
1285
- });
1286
- if (response._tag !== "Semantic") return yield* MemoryRpcError.make({ reason: "protocol" });
1287
- return response.result;
1288
- }).pipe((effect) => withinDeadline(effect, validated.timeoutMillis));
1289
- });
1290
- return {
1291
- recall: Effect.fn("CloudflareMemoryClient.recall")(function* (lookup, limits, estimateTokens) {
1292
- return yield* Memory.recall([{
1293
- id: "memory",
1294
- essential: true,
1295
- read: revalidate(lookup, limits)
1296
- }], limits, estimateTokens);
1297
- }),
1298
- revalidate,
1299
- revalidateSemantic,
1300
- change
1301
- };
1302
- });
1303
- const CloudflareMemoryClient = {
1304
- /** Bind access and principal using the MemoryObjectNamespace supplied by the application. */
1305
- make: makeMemoryClient,
1306
- /** Use a resolved Worker or Durable Object binding without manual service provisioning. */
1307
- fromBinding: Effect.fn("CloudflareMemoryClient.fromBinding")(function* (binding, options) {
1308
- return yield* makeMemoryClient(options.access, options.principal, options.rpcLimits).pipe(Effect.provideService(MemoryObjectNamespace, { namespace: binding }));
1309
- })
1310
- };
1311
- /**
1312
- * Optional activity-processor destination. Keeps domain write errors intact; transport,
1313
- * authorization and deadline failures become the existing MemoryStorageError contract.
1314
- * Receipts remain authoritative, including after caller interruption or lost replies.
1315
- */
1316
- const cloudflareMemoryWriterLayer = (access, principal, limits = defaultMemoryRpcLimits) => Layer.effect(MemoryWriter, Effect.gen(function* () {
1317
- const client = yield* CloudflareMemoryClient.make(access, principal, limits);
1318
- return MemoryWriter.fromAdapter({ change: (write) => client.change(write).pipe(Effect.catchTag("MemoryRpcError", (error) => Effect.fail(MemoryStorageError.make({
1319
- operation: `memory RPC ${error.reason}`,
1320
- reason: error.reason === "unavailable" || error.reason === "timeout" ? "unavailable" : "invalid-input"
1321
- }))), Effect.catchTag([
1322
- "MemoryRecallError",
1323
- "MemoryIndexError",
1324
- "SemanticMemoryError"
1325
- ], () => Effect.fail(MemoryStorageError.make({
1326
- operation: "memory RPC response",
1327
- reason: "corrupt"
1328
- })))) });
1329
- }));
1330
- /**
1331
- * Dedicated SQLite owner, independent of Thread lifetimes. The host binds authorization
1332
- * after restoring its namespace definition from MemoryOwnerIdentity. Do not retain
1333
- * cleanup-scoped resources in the host Layer; it lives for the DO incarnation.
1334
- */
1335
- const makeMemoryObject = (host, options = {}) => {
1336
- const identity = Layer.effect(MemoryOwnerIdentity, Effect.gen(function* () {
1337
- const state = yield* DurableObjectState.DurableObjectState;
1338
- const address = yield* Schema.decodeUnknownEffect(MemoryNamespaceAddress)(state.raw.id.name).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "denied" })));
1339
- return { namespace: MemoryNamespace.Any.make({ address }) };
1340
- }));
1341
- const store = Layer.unwrap(Effect.map(DurableObjectState.DurableObjectState, (state) => doMemoryStoreLayerWithFailpoints(state.raw.storage, options.storageLimits ?? defaultDoMemoryStorageLimits))).pipe(Layer.provide(options.failpoints ?? MemoryMutationFailpoint.layer));
1342
- const application = Layer.merge(store, host).pipe(Layer.provideMerge(identity));
1343
- const runtime = Layer.effectContext(Effect.gen(function* () {
1344
- const state = yield* DurableObjectState.DurableObjectState;
1345
- const scope = yield* Effect.scope;
1346
- yield* Schema.decodeUnknownEffect(MemoryRpcLimits)(options.rpcLimits ?? defaultMemoryRpcLimits).pipe(Effect.mapError(() => MemoryRpcError.make({ reason: "protocol" })));
1347
- return yield* state.blockConcurrencyWhile(Layer.buildWithScope(application, scope));
1348
- }));
1349
- return DurableObject.make(runtime, { rpc: { memory: (encoded) => handleMemoryOwnerRequest(encoded, options.rpcLimits) } });
1350
- };
1351
- const MemoryObject = {
1352
- /** Build the SQLite Durable Object class with the application's owner authorization Layer. */
1353
- make: makeMemoryObject };
1354
- //#endregion
1355
- export { AbortRecorded, AdmissionLimitExceeded, ApprovalRecorded, AwaitProgressRequest, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CancelProgressRequest, CloudflareAdmissionLimitsValue, CloudflareBindingError, CloudflareBrowser, CloudflareCodeMode, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflareMemoryClient, CloudflarePlatformConfigError, CloudflareSchedulingClient, CloudflareSubscriptionConfigError, CloudflareSubscriptionsClient, CloudflareThreadClient, DEFAULT_MAX_DATABASE_BYTES, DurableAlarmError, DurableAlarmService, DurableObjectContext, HostFailed, HostFailure, HostProtocolError, HostResponse, MaintenancePassReport, MemoryObject, MemoryObjectNamespace, ObservePageRequest, ObservedPage, ProgressCancelled, ProgressObserved, ProgressWaitRegistry, ScheduleAlarmProtocolError, ScheduleOwnerIdentity, ScheduleOwnerNamespace, ScheduleOwnerProtocolError, SettlementReached, SubmitRequest, SubmitSucceeded, SubscriptionAlarmProtocolError, SubscriptionPartitionIdentity, SubscriptionPartitionNamespace, SubscriptionPartitionProtocolError, ThreadClientError, ThreadMaintenance, ThreadMaintenanceFailpoint, thread_object_exports as ThreadObject, ThreadObjectIdentity, ThreadObjectNamespace, ThreadObjectPorts, UnknownResolutionRecorded, boundHostDiagnostic, cloudflareMemoryWriterLayer, cloudflarePreparedInputAdmissionLayer, cloudflareScheduledInputAdmissionLayer, cloudflareWakeSchedulerLayer, decodeAbortCommand, decodeApprovalDecisionCommand, decodeAwaitProgressRequest, decodeCancelProgressRequest, decodeHostResponse, decodeObservePageRequest, decodeReceipt, decodeSubmitRequest, decodeUnknownResolutionCommand, disposeRpcHandle, dynamicWorkerCodeExecutorLayer, dynamicWorkerImplementation, encodeAbortCommand, encodeApprovalDecisionCommand, encodeAwaitProgressRequest, encodeCancelProgressRequest, encodeHostResponse, encodeObservePageRequest, encodeReceipt, encodeSubmitRequest, encodeUnknownResolutionCommand, layerFromBindings, makeScheduleOwnerObjectClass, makeSubscriptionPartitionObjectClass, memoryObjectName, sourcePartitionName, threadNamespaceFromEnv, threadNamespaceLayer, threadPortTransportLayer, validateCloudflareSubscriptionLimits };
1356
-
1357
- //# sourceMappingURL=index.mjs.map
1
+ import { t as CloudflareBindings_exports } from "./CloudflareBindings.mjs";
2
+ import { t as CloudflareConfig_exports } from "./CloudflareConfig.mjs";
3
+ import { t as Alarm_exports } from "./Alarm.mjs";
4
+ import { t as CloudflareBrowser_exports } from "./CloudflareBrowser-1jeZx3_g.mjs";
5
+ import { t as CloudflareCodeMode_exports } from "./CloudflareCodeMode.mjs";
6
+ import { t as CloudflareMemory_exports } from "./CloudflareMemory.mjs";
7
+ import { t as CloudflareThreadClient_exports } from "./CloudflareThreadClient.mjs";
8
+ import { t as CloudflareScheduling_exports } from "./CloudflareScheduling.mjs";
9
+ import { t as CloudflareSubscriptions_exports } from "./CloudflareSubscriptions.mjs";
10
+ import { t as WakeScheduler_exports } from "./WakeScheduler.mjs";
11
+ import { l as ThreadObject_exports } from "./ThreadObject-IrGuIO4a.mjs";
12
+ export { Alarm_exports as Alarm, CloudflareBindings_exports as CloudflareBindings, CloudflareBrowser_exports as CloudflareBrowser, CloudflareCodeMode_exports as CloudflareCodeMode, CloudflareConfig_exports as CloudflareConfig, CloudflareMemory_exports as CloudflareMemory, CloudflareScheduling_exports as CloudflareScheduling, CloudflareSubscriptions_exports as CloudflareSubscriptions, CloudflareThreadClient_exports as CloudflareThreadClient, ThreadObject_exports as ThreadObject, WakeScheduler_exports as WakeScheduler };