@effect-agent/platform-cloudflare 0.0.1-beta.5 → 0.0.1-beta.6

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/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
- import { Clock, Context, Duration, Effect, Layer, ManagedRuntime, Option, PubSub, Random, Ref, Schema, Stream } from "effect";
1
+ import { Cause, Clock, Context, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, Option, PubSub, Random, Ref, Schema, Stream } from "effect";
2
2
  import { AbortCommand, AbortIntent, AdmissionConflict, AgentBindingResolver, AppendConflict, ApprovalConflict, ApprovalDecisionCommand, ApprovalDecisionIntent, CanonicalRecordEnvelope, CanonicalSequence, ConversationNotMaterialized, ConversationRead, ConversationStore, ConversationStoreError, DEFAULT_OWNERSHIP_LEASE_DURATION, DefinitionDigests, DeploymentId, DigestError, DurableAgentRuntime, DurableRuntimeConfig, DurableRuntimeFailpoint, DurableRuntimeFailpointError, FenceRejected, IdempotencyKey, IntegrityReport, JoinedToHost, LedgerError, ObligationReport, ObligationThresholds, OperationAuthorizationRequest, OperationAuthorizer, OperationDenied, OwnershipLost, PersistedJson, Principal, ProducerId, Receipt, RecoveryExplanation, RecoveryReport, RetryCommand, RetryRefused, RunJournalError, Settlement, SettlementConflict, SubmissionLedger, SubmissionLookupByKey, ToolReconciler, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, WakeScheduler } from "@effect-agent/session";
3
3
  import { ConversationPortTransport, DEFAULT_MAX_STORED_VALUE_BYTES, conversationStoreLayer, handleEncodedPortRequest, portTransportFailure, routedConversationStoreLayer, routedSubmissionLedgerLayer, storageConfigLayer, storageFailpointLayer, submissionLedgerLayer } from "@effect-agent/storage-cloudflare";
4
4
  import { AgentId, AgentInputError, ConversationId, SubmissionId } from "@effect-agent/core";
5
5
  import { BrowserCrypto } from "@effect/platform-browser";
6
6
  import { SqliteClient } from "@effect/sql-sqlite-do";
7
- import { DurableObject } from "cloudflare:workers";
7
+ import { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
8
+ import { CodeExecutionHost, CodeExecutionProtocolError, CodeExecutionResourceUse, CodeExecutionResult, CodeExecutionTimeoutError, CodeExecutor, CodeExecutorStartError, CodeExecutorTerminatedError, CodeExecutorUnsupportedError, CodeHostCall, CodeHostCallLimitError, CodeOutputLimitError, CodeProgramFailedError, CodeSourceError, SandboxImplementation } from "@effect-agent/sandbox";
8
9
  //#region src/bindings.ts
9
10
  /**
10
11
  * Cloudflare platform bindings as Effect services (DEPLOY-010: "Cloudflare platform bindings
@@ -146,6 +147,8 @@ var CloudflareDurableRuntimeConfigValue = class extends Schema.Class("@effect-ag
146
147
  abortPollInterval: PositiveMillis,
147
148
  /** Canonical observation poll cadence of the Durable Object store. */
148
149
  observationPollInterval: NonNegativeMillis,
150
+ /** Cooperative background-export budget; native Object delivery never awaits the flush. */
151
+ telemetryFlushTimeout: PositiveMillis,
149
152
  /** Per-value byte bound; must stay under the platform's 2 MB SQLite value limit. */
150
153
  maxStoredValueBytes: Schema.Int.check(Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(2e6)),
151
154
  /** Opt-in full payload/digest-chain audit while opening the store. */
@@ -164,6 +167,7 @@ const CLOUDFLARE_RUNTIME_DEFAULTS = {
164
167
  leaseRenewalInterval: 1e4,
165
168
  abortPollInterval: 500,
166
169
  observationPollInterval: 25,
170
+ telemetryFlushTimeout: 2e3,
167
171
  maxStoredValueBytes: DEFAULT_MAX_STORED_VALUE_BYTES,
168
172
  verifyOnOpen: false,
169
173
  maxQueueDepthPerLane: 256,
@@ -411,6 +415,31 @@ const conversationPortTransportLayer = Layer.effect(ConversationPortTransport)(E
411
415
  }).pipe(Effect.withSpan("CloudflarePortTransport.call", { attributes: { conversationId } })) });
412
416
  }));
413
417
  //#endregion
418
+ //#region src/telemetry.ts
419
+ /** A host exporter failed during one coalesced background export attempt. */
420
+ var CloudflareTelemetryExportError = class extends Schema.TaggedError()("CloudflareTelemetryExportError", {
421
+ /** The original exporter failure, retained for host-side diagnostics. */
422
+ cause: Schema.Defect() }) {};
423
+ /**
424
+ * Host-owned exporter lifecycle for one Cloudflare Conversation Object incarnation.
425
+ *
426
+ * The platform package creates measurements but deliberately does not select an exporter or
427
+ * vendor. Native entrypoint instrumentation consumes this service at the Worker composition edge;
428
+ * applications pass its provider as the second `makeConversationObjectClass` argument together
429
+ * with their Effect Logger, Tracer, and Metric layers. That provider may read `DurableObjectContext` or
430
+ * `ConversationObjectNamespace`, and its typed acquisition error remains visible in construction.
431
+ * `flush` must attempt to export every configured signal. After the native span closes, the
432
+ * Conversation Object reserves it into a shared post-settlement batch before `ctx.waitUntil` under
433
+ * a cooperative timeout. Only each pending/trailing/queued batch's first owner registers its
434
+ * shared Promise. Cycles remain capped at one first exporter attempt plus at most one trailing
435
+ * attempt, with one queued cycle while trailing runs; exporter completion never holds endpoint
436
+ * delivery open and exporter failure never replaces endpoint behavior.
437
+ */
438
+ var CloudflareRuntimeTelemetry = class CloudflareRuntimeTelemetry extends Context.Service()("@effect-agent/platform-cloudflare/CloudflareRuntimeTelemetry") {
439
+ /** Content-free default selected by `makeConversationObjectClass` when no host Layer is passed. */
440
+ static layerNoop = Layer.succeed(CloudflareRuntimeTelemetry)({ flush: Effect.void });
441
+ };
442
+ //#endregion
414
443
  //#region src/layers.ts
415
444
  /**
416
445
  * Owner-side endpoint body for the Conversation Object's `portCall` (plan §1.3): decode,
@@ -433,6 +462,7 @@ const configFromOptions = (options) => decodeConfigValue({
433
462
  leaseRenewalInterval: options.leaseRenewalInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.leaseRenewalInterval,
434
463
  abortPollInterval: options.abortPollInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.abortPollInterval,
435
464
  observationPollInterval: options.observationPollInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.observationPollInterval,
465
+ telemetryFlushTimeout: options.telemetryFlushTimeout ?? CLOUDFLARE_RUNTIME_DEFAULTS.telemetryFlushTimeout,
436
466
  maxStoredValueBytes: options.maxStoredValueBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxStoredValueBytes,
437
467
  verifyOnOpen: options.verifyOnOpen ?? CLOUDFLARE_RUNTIME_DEFAULTS.verifyOnOpen,
438
468
  limits: {
@@ -470,9 +500,9 @@ const resolveBindings = (source, context) => source === void 0 ? Effect.succeed(
470
500
  * Storage compatibility is verified during construction: an incompatible database fails the
471
501
  * Layer typed (`DoStorageCompatibilityError`) before anything is mutated (DEPLOY-008).
472
502
  *
473
- * Requires only the two binding services (`DurableObjectContext`,
474
- * `ConversationObjectNamespace`) — platform values enter exclusively through Layers
475
- * (DEPLOY-010).
503
+ * Requires the two binding services (`DurableObjectContext`, `ConversationObjectNamespace`).
504
+ * Host observability belongs to the Worker composition edge because native entrypoint lifecycle
505
+ * instrumentation, rather than this durable runtime assembly, consumes it (DEPLOY-010).
476
506
  */
477
507
  var CloudflareDurableRuntime = class {
478
508
  static layer(options) {
@@ -778,6 +808,258 @@ var CloudflareConversationClient = class CloudflareConversationClient extends Co
778
808
  }));
779
809
  };
780
810
  //#endregion
811
+ //#region src/telemetry-internal.ts
812
+ /** Bounded typed control signal used only while a native entrypoint span is active. */
813
+ var CloudflareNativeSpanFailure = class extends Error {
814
+ name = "ConversationObjectDeliveryFailed";
815
+ constructor() {
816
+ super("Cloudflare Conversation Object delivery failed");
817
+ }
818
+ };
819
+ const stripCloudflareNativeSpanFailures = (cause) => {
820
+ if (!cause.reasons.some((reason) => Cause.isFailReason(reason) && reason.error instanceof CloudflareNativeSpanFailure)) return {
821
+ found: false,
822
+ residual: cause
823
+ };
824
+ const reasons = cause.reasons.filter((reason) => {
825
+ if (Cause.isFailReason(reason) && reason.error instanceof CloudflareNativeSpanFailure) return false;
826
+ return true;
827
+ });
828
+ return {
829
+ found: true,
830
+ residual: Cause.fromReasons(reasons)
831
+ };
832
+ };
833
+ /**
834
+ * @internal Replace an entrypoint Cause with a bounded typed marker inside a span, then remove
835
+ * only that marker and restore the exact invocation-local Cause plus any newly composed reasons.
836
+ */
837
+ const withCloudflareNativeSpanFailure = (effect, trace) => Effect.suspend(() => {
838
+ let original;
839
+ return trace(effect.pipe(Effect.catchCause((cause) => {
840
+ original = cause;
841
+ return Effect.fail(new CloudflareNativeSpanFailure());
842
+ }))).pipe(Effect.catchCause((cause) => {
843
+ const { found, residual } = stripCloudflareNativeSpanFailures(cause);
844
+ if (!found) return Effect.failCause(residual);
845
+ const restored = original === void 0 ? residual : Cause.combine(original, residual);
846
+ return Effect.failCause(restored);
847
+ }));
848
+ });
849
+ const logCloudflareTelemetryCause = (cause) => {
850
+ const kinds = /* @__PURE__ */ new Set();
851
+ for (const reason of cause.reasons) if (Cause.isFailReason(reason)) kinds.add(Cause.isTimeoutError(reason.error) ? "timeout" : "exporter");
852
+ else if (Cause.isDieReason(reason)) kinds.add("defect");
853
+ else kinds.add("interrupted");
854
+ return Effect.forEach(kinds, (kind) => {
855
+ return (kind === "defect" ? Effect.logError("Cloudflare telemetry flush defect") : Effect.logWarning(kind === "exporter" ? "Cloudflare telemetry exporter failed" : kind === "timeout" ? "Cloudflare telemetry flush exceeded its time budget" : "Cloudflare telemetry flush interrupted")).pipe(Effect.annotateLogs({ "effect_agent.cloudflare.telemetry.failure_kind": kind }));
856
+ }, { discard: true });
857
+ };
858
+ /**
859
+ * @internal Cooperative background flush budget. Interruptible exporters stop at the configured
860
+ * timeout; uninterruptible exporters remain bounded by Cloudflare's waitUntil lifecycle instead
861
+ * of holding the native delivery open.
862
+ */
863
+ const flushCloudflareRuntimeTelemetry = (timeoutMillis) => Effect.flatMap(CloudflareRuntimeTelemetry, ({ flush }) => flush).pipe(Effect.interruptible, Effect.timeout(Duration.millis(timeoutMillis)), Effect.onExit((exit) => Exit.isFailure(exit) ? logCloudflareTelemetryCause(exit.cause) : Effect.void), Effect.asVoid);
864
+ /** @internal Convert the shared exporter result into the always-fulfilled waitUntil bridge. */
865
+ const fulfillCloudflareTelemetryBackground = (result) => result.then(() => void 0, () => void 0);
866
+ const makeFlushBatch = () => {
867
+ let resolveResult = () => void 0;
868
+ let rejectResult = (_cause) => void 0;
869
+ const result = new Promise((resolve, reject) => {
870
+ resolveResult = resolve;
871
+ rejectResult = reject;
872
+ });
873
+ let resolveReady = () => void 0;
874
+ const ready = new Promise((resolve) => {
875
+ resolveReady = resolve;
876
+ });
877
+ return {
878
+ result,
879
+ background: fulfillCloudflareTelemetryBackground(result),
880
+ resolveResult,
881
+ rejectResult,
882
+ ready,
883
+ resolveReady,
884
+ phase: "pending",
885
+ reservations: 0,
886
+ unsettledDeliveries: 0,
887
+ dropDiagnosed: false
888
+ };
889
+ };
890
+ const makeFlushCycle = () => {
891
+ let resolveCycle = () => void 0;
892
+ let rejectCycle = (_cause) => void 0;
893
+ const promise = new Promise((resolve, reject) => {
894
+ resolveCycle = resolve;
895
+ rejectCycle = reject;
896
+ });
897
+ fulfillCloudflareTelemetryBackground(promise);
898
+ return {
899
+ promise,
900
+ resolve: resolveCycle,
901
+ reject: rejectCycle,
902
+ first: makeFlushBatch(),
903
+ phase: "pending",
904
+ trailing: void 0
905
+ };
906
+ };
907
+ /**
908
+ * @internal Coordinate exporter attempts for one Conversation Object incarnation. Each cycle is
909
+ * capped at a first attempt plus one trailing attempt. Each batch is reserved synchronously and
910
+ * waits for every delivery assigned to it before exporting. Only the first owner of the pending,
911
+ * trailing, or queued batch registers that batch's shared background Promise, so a stalled
912
+ * exporter cannot accumulate per-delivery waitUntil registrations. Each batch retains at most
913
+ * `MAX_CLOUDFLARE_TELEMETRY_BATCH_DELIVERIES` settlement Promises; excess deliveries are lossy-
914
+ * coalesced into the already-requested export without settlement ownership and diagnosed once for
915
+ * that batch. Failures remain rejected by the raw cycle after every requested attempt runs; each
916
+ * shared waitUntil bridge always fulfills.
917
+ */
918
+ const makeCloudflareTelemetryFlushCoordinator = (flush, options = {}) => {
919
+ let active;
920
+ let queued;
921
+ const closeBatch = (batch) => {
922
+ if (batch.phase !== "pending") return;
923
+ batch.phase = "waiting";
924
+ if (batch.unsettledDeliveries === 0) batch.resolveReady();
925
+ };
926
+ const cancelBatch = (batch) => {
927
+ if (batch.phase === "settled" || batch.phase === "cancelled") return;
928
+ batch.phase = "cancelled";
929
+ batch.resolveReady();
930
+ batch.resolveResult();
931
+ };
932
+ const reserveDelivery = (batch, delivery) => {
933
+ if (batch.reservations >= 64) {
934
+ if (!batch.dropDiagnosed) {
935
+ batch.dropDiagnosed = true;
936
+ try {
937
+ options.onReservationDropped?.();
938
+ } catch {}
939
+ }
940
+ return {
941
+ owner: false,
942
+ dropped: true
943
+ };
944
+ }
945
+ const owner = batch.reservations === 0;
946
+ batch.reservations += 1;
947
+ batch.unsettledDeliveries += 1;
948
+ const settled = () => {
949
+ batch.unsettledDeliveries -= 1;
950
+ if (batch.phase === "waiting" && batch.unsettledDeliveries === 0) batch.resolveReady();
951
+ };
952
+ delivery.then(settled, settled);
953
+ return {
954
+ owner,
955
+ dropped: false
956
+ };
957
+ };
958
+ const runCycle = async (cycle) => {
959
+ const failures = [];
960
+ const runAttempt = async () => {
961
+ try {
962
+ await flush();
963
+ } catch (cause) {
964
+ failures.push(cause);
965
+ }
966
+ };
967
+ const runBatch = async (batch) => {
968
+ closeBatch(batch);
969
+ await batch.ready;
970
+ if (batch.phase === "cancelled") return;
971
+ batch.phase = "running";
972
+ const failureStart = failures.length;
973
+ await runAttempt();
974
+ batch.phase = "settled";
975
+ if (failures.length === failureStart) batch.resolveResult();
976
+ else batch.rejectResult(failures[failures.length - 1]);
977
+ };
978
+ try {
979
+ cycle.phase = "first";
980
+ await runBatch(cycle.first);
981
+ const trailing = cycle.trailing;
982
+ if (trailing !== void 0) {
983
+ cycle.phase = "trailing";
984
+ await runBatch(trailing);
985
+ }
986
+ cycle.phase = "settling";
987
+ options.onSettling?.();
988
+ } finally {
989
+ const next = queued;
990
+ queued = void 0;
991
+ active = next;
992
+ if (next !== void 0) startCycle(next);
993
+ }
994
+ if (failures.length === 0) return;
995
+ if (failures.length === 1) throw failures[0];
996
+ throw new AggregateError(failures, `${failures.length} Cloudflare telemetry flush attempts failed`);
997
+ };
998
+ const startCycle = (cycle) => {
999
+ Promise.resolve().then(() => runCycle(cycle)).then(cycle.resolve, cycle.reject);
1000
+ };
1001
+ const reserve = (delivery) => {
1002
+ let cycle;
1003
+ let batch;
1004
+ let shouldStart = false;
1005
+ if (active === void 0) {
1006
+ cycle = makeFlushCycle();
1007
+ active = cycle;
1008
+ batch = cycle.first;
1009
+ shouldStart = true;
1010
+ } else if (active.phase === "pending" && active.first.phase !== "cancelled") {
1011
+ cycle = active;
1012
+ batch = active.first;
1013
+ } else if (active.phase === "first") {
1014
+ cycle = active;
1015
+ batch = active.trailing ??= makeFlushBatch();
1016
+ } else {
1017
+ if (queued === void 0) queued = makeFlushCycle();
1018
+ cycle = queued;
1019
+ batch = queued.first;
1020
+ }
1021
+ const { dropped, owner } = reserveDelivery(batch, delivery);
1022
+ if (shouldStart) startCycle(cycle);
1023
+ return {
1024
+ background: batch.background,
1025
+ cycle: cycle.promise,
1026
+ owner,
1027
+ dropped,
1028
+ cancel: () => {
1029
+ if (!owner) return;
1030
+ cancelBatch(batch);
1031
+ if (cycle.trailing === batch && cycle.phase === "first") cycle.trailing = void 0;
1032
+ else if (queued === cycle && active !== cycle) {
1033
+ queued = void 0;
1034
+ cycle.resolve();
1035
+ }
1036
+ }
1037
+ };
1038
+ };
1039
+ return { reserve };
1040
+ };
1041
+ /**
1042
+ * @internal Reserve telemetry before registration without allowing a synchronous platform failure
1043
+ * to replace the already-running native delivery Promise. Only a batch owner calls waitUntil; its
1044
+ * shared bridge is already always fulfilled, and registration failure cancels export for that
1045
+ * unowned batch.
1046
+ */
1047
+ const registerCloudflareTelemetryAfterNativeSettlement = (waitUntil, delivery, reserve, diagnoseRegistrationFailure) => {
1048
+ const reservation = reserve(delivery);
1049
+ if (!reservation.owner) return delivery;
1050
+ try {
1051
+ waitUntil(reservation.background);
1052
+ } catch (cause) {
1053
+ reservation.cancel();
1054
+ try {
1055
+ diagnoseRegistrationFailure(cause);
1056
+ } catch {}
1057
+ }
1058
+ return delivery;
1059
+ };
1060
+ /** @internal Content-free diagnostic for a rejected Cloudflare waitUntil registration. */
1061
+ const logCloudflareWaitUntilRegistrationFailure = (_cause) => Effect.logError("Cloudflare telemetry waitUntil registration failed").pipe(Effect.annotateLogs({ "effect_agent.cloudflare.telemetry.failure_kind": "wait_until_registration" }));
1062
+ //#endregion
781
1063
  //#region src/conversation-object.ts
782
1064
  /** Port envelope tags whose owner-side execution durably mutates this Object's lane. */
783
1065
  const MUTATING_PORT_TAGS = /* @__PURE__ */ new Set([
@@ -1017,62 +1299,643 @@ const gateEndpoint = Effect.gen(function* () {
1017
1299
  yield* (yield* ConversationMaintenance).ensureAlarm;
1018
1300
  });
1019
1301
  /**
1020
- * Build the application's Conversation Object class (export it from the
1021
- * Worker entry). The explicit return type is what makes declaration emit
1022
- * possible: the class body carries a private runtime field, and TS4094
1023
- * rejects inferring an exported anonymous class type around it.
1302
+ * Owner-side delivery measurement. The exact endpoint Cause is hidden behind a bounded typed
1303
+ * marker while the span closes, then restored together with any newly composed real reasons.
1304
+ */
1305
+ const observeNativeEntrypoint = (entrypoint, effect) => Effect.gen(function* () {
1306
+ const identity = yield* ConversationObjectIdentity;
1307
+ const config = yield* CloudflareDurableRuntimeConfig;
1308
+ return yield* withCloudflareNativeSpanFailure(effect, (masked) => masked.pipe(Effect.withSpan(`effect_agent.cloudflare.conversation_object.${entrypoint}`, {
1309
+ kind: "server",
1310
+ attributes: {
1311
+ "effect_agent.cloudflare.entrypoint": entrypoint,
1312
+ "effect_agent.deployment.id": config.deploymentId,
1313
+ conversationId: identity.conversationId,
1314
+ producerId: identity.producerId
1315
+ }
1316
+ })));
1317
+ });
1318
+ const flushNativeEntrypointTelemetry = Effect.flatMap(CloudflareDurableRuntimeConfig, (config) => flushCloudflareRuntimeTelemetry(config.telemetryFlushTimeout));
1319
+ /**
1320
+ * Build the application's Conversation Object class (export it from the Worker entry).
1321
+ * `telemetry` is the host composition boundary: it may require the Object context/namespace and
1322
+ * retain a typed acquisition error, while any additional output services remain available to the
1323
+ * cached runtime. `TelemetryError` stays the first explicit generic for source compatibility;
1324
+ * additional outputs infer from the Layer. Omitting it installs the content-free no-op service.
1325
+ * The explicit return type is what makes declaration emit possible: the class body carries a
1326
+ * private runtime field, and TS4094 rejects inferring an exported anonymous class type around it.
1024
1327
  */
1025
- const makeConversationObjectClass = (options) => {
1328
+ const makeConversationObjectClass = (options, telemetry) => {
1026
1329
  class ConversationObject extends DurableObject {
1027
1330
  #runtime;
1331
+ #ctx;
1332
+ #telemetryFlush;
1028
1333
  constructor(ctx, env) {
1029
1334
  super(ctx, env);
1030
- this.#runtime = ManagedRuntime.make(CloudflareDurableRuntime.layer(options).pipe(Layer.provideMerge(Layer.mergeAll(DurableObjectContext.layer(ctx, env), conversationNamespaceLayer(env, options.namespaceBinding)))));
1335
+ this.#ctx = ctx;
1336
+ const platform = Layer.mergeAll(DurableObjectContext.layer(ctx, env), conversationNamespaceLayer(env, options.namespaceBinding));
1337
+ const hostTelemetry = (telemetry ?? CloudflareRuntimeTelemetry.layerNoop).pipe(Layer.provideMerge(platform));
1338
+ this.#runtime = ManagedRuntime.make(CloudflareDurableRuntime.layer(options).pipe(Layer.provideMerge(hostTelemetry)));
1339
+ this.#telemetryFlush = makeCloudflareTelemetryFlushCoordinator(() => this.#runtime.runPromise(flushNativeEntrypointTelemetry), { onReservationDropped: () => {
1340
+ this.#runtime.runSyncExit(Effect.logWarning("Cloudflare telemetry delivery coalesced at the batch reservation limit").pipe(Effect.annotateLogs({ "effect_agent.cloudflare.telemetry.failure_kind": "reservation_limit" })));
1341
+ } });
1031
1342
  ctx.blockConcurrencyWhile(() => this.#runtime.runPromise(gateEndpoint));
1032
1343
  }
1344
+ #runNative(entrypoint, effect) {
1345
+ return registerCloudflareTelemetryAfterNativeSettlement((background) => this.#ctx.waitUntil(background), this.#runtime.runPromise(observeNativeEntrypoint(entrypoint, effect)), this.#telemetryFlush.reserve, (cause) => {
1346
+ this.#runtime.runSyncExit(logCloudflareWaitUntilRegistrationFailure(cause));
1347
+ });
1348
+ }
1033
1349
  async submitEncoded(encoded) {
1034
- return this.#runtime.runPromise(submitEndpoint(encoded));
1350
+ return this.#runNative("submit", submitEndpoint(encoded));
1035
1351
  }
1036
1352
  async awaitSettlementEncoded(encoded) {
1037
- return this.#runtime.runPromise(awaitSettlementEndpoint(encoded));
1353
+ return this.#runNative("await_settlement", awaitSettlementEndpoint(encoded));
1038
1354
  }
1039
1355
  async observePage(encoded) {
1040
- return this.#runtime.runPromise(observePageEndpoint(encoded));
1356
+ return this.#runNative("observe", observePageEndpoint(encoded));
1041
1357
  }
1042
1358
  async abortEncoded(encoded) {
1043
- return this.#runtime.runPromise(abortEndpoint(encoded));
1359
+ return this.#runNative("abort", abortEndpoint(encoded));
1044
1360
  }
1045
1361
  async resolveApprovalEncoded(encoded) {
1046
- return this.#runtime.runPromise(resolveApprovalEndpoint(encoded));
1362
+ return this.#runNative("resolve_approval", resolveApprovalEndpoint(encoded));
1047
1363
  }
1048
1364
  async resolveUnknownEncoded(encoded) {
1049
- return this.#runtime.runPromise(resolveUnknownEndpoint(encoded));
1365
+ return this.#runNative("resolve_unknown", resolveUnknownEndpoint(encoded));
1050
1366
  }
1051
1367
  async explainEncoded(encoded) {
1052
- return this.#runtime.runPromise(explainEndpoint(encoded));
1368
+ return this.#runNative("explain", explainEndpoint(encoded));
1053
1369
  }
1054
1370
  async verifyEncoded(encoded) {
1055
- return this.#runtime.runPromise(verifyEndpoint(encoded));
1371
+ return this.#runNative("verify", verifyEndpoint(encoded));
1056
1372
  }
1057
1373
  async retryEncoded(encoded) {
1058
- return this.#runtime.runPromise(retryEndpoint(encoded));
1374
+ return this.#runNative("retry", retryEndpoint(encoded));
1059
1375
  }
1060
1376
  async obligationsEncoded(encoded) {
1061
- return this.#runtime.runPromise(obligationsEndpoint(encoded));
1377
+ return this.#runNative("obligations", obligationsEndpoint(encoded));
1062
1378
  }
1063
1379
  async portCall(encoded) {
1064
- return this.#runtime.runPromise(portCallEndpoint(encoded));
1380
+ return this.#runNative("port_call", portCallEndpoint(encoded));
1065
1381
  }
1066
1382
  async wake() {
1067
- await this.#runtime.runPromise(wakeEndpoint);
1383
+ await this.#runNative("wake", wakeEndpoint);
1068
1384
  }
1069
1385
  async alarm() {
1070
- await this.#runtime.runPromise(alarmEndpoint);
1386
+ await this.#runNative("alarm", alarmEndpoint);
1071
1387
  }
1072
1388
  }
1073
1389
  return ConversationObject;
1074
1390
  };
1075
1391
  //#endregion
1076
- export { AbortRecorded, AdminExplainRequest, AdminFailed, AdminFailure, AdminResponse, AdminVerifyRequest, AdmissionLimitExceeded, ApprovalRecorded, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CloudflareAdmissionLimitsValue, CloudflareBindingError, CloudflareConversationClient, CloudflareDurableRuntime, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError, ConversationClientError, ConversationMaintenance, ConversationObjectIdentity, ConversationObjectNamespace, ConversationObjectPorts, DEFAULT_MAX_DATABASE_BYTES, DurableAlarmError, DurableAlarmService, DurableObjectContext, ExplainedRecovery, HostFailed, HostFailure, HostProtocolError, HostResponse, MaintenancePassReport, ObligationsScanned, ObservePageRequest, ObservedPage, RetryExecuted, SettlementReached, SubmitRequest, SubmitSucceeded, UnknownResolutionRecorded, VerifiedIntegrity, boundHostDiagnostic, cloudflareWakeSchedulerLayer, conversationNamespaceFromEnv, conversationNamespaceLayer, conversationPortTransportLayer, decodeAbortCommand, decodeAdminExplainRequest, decodeAdminResponse, decodeAdminVerifyRequest, decodeApprovalDecisionCommand, decodeHostResponse, decodeObligationThresholds, decodeObservePageRequest, decodeReceipt, decodeRetryCommand, decodeSubmitRequest, decodeUnknownResolutionCommand, encodeAbortCommand, encodeAdminResponse, encodeApprovalDecisionCommand, encodeHostResponse, encodeObservePageRequest, encodeReceipt, encodeSubmitRequest, encodeUnknownResolutionCommand, makeConversationObjectClass };
1392
+ //#region src/code-mode-executor.ts
1393
+ /**
1394
+ * The Cloudflare Dynamic Worker `CodeExecutor` adapter (C4 of ADR-0017;
1395
+ * DEPLOY-011). Each pass loads one fresh Worker through the Worker Loader
1396
+ * with `globalOutbound: null`, so generated code has no ambient network,
1397
+ * bindings, or secrets; its only authority is the pass-scoped host stub that
1398
+ * routes back to `CodeModeHostEntrypoint` and, from there, into the pass's
1399
+ * `CodeExecutionHost` service. Platform CPU limits stop synchronous runaway
1400
+ * programs; the executor-owned wall-clock deadline interrupts asynchronously
1401
+ * suspended passes. Deployment class `E` only: the adapter records no
1402
+ * persistent state and a later pass may run in a completely different
1403
+ * isolate.
1404
+ */
1405
+ const dynamicWorkerImplementation = SandboxImplementation.make({
1406
+ isolation: "isolated",
1407
+ identity: "cloudflare-dynamic-worker"
1408
+ });
1409
+ /**
1410
+ * Live passes by identity. Entries are Scope-managed: registered when a pass
1411
+ * opens and removed by its finalizer, so a stale harness (or a forged
1412
+ * `passId`) cannot reach any host authority.
1413
+ */
1414
+ const passRegistry = /* @__PURE__ */ new Map();
1415
+ /**
1416
+ * The host-side RPC target for dynamic workers. The application exposes it
1417
+ * from its Worker entry (`export { CodeModeHostEntrypoint }`) and hands the
1418
+ * adapter a same-instance stub — `ctx.exports.CodeModeHostEntrypoint()` in
1419
+ * production (a self service binding may reach a different instance and must
1420
+ * not be used there); tests bind it through Miniflare's `kCurrentWorker`.
1421
+ */
1422
+ var CodeModeHostEntrypoint = class extends WorkerEntrypoint {
1423
+ async call(passId, hostCall) {
1424
+ const pass = passRegistry.get(String(passId));
1425
+ if (pass === void 0) throw new Error("Unknown Code Mode pass");
1426
+ return pass.dispatch(hostCall);
1427
+ }
1428
+ };
1429
+ /**
1430
+ * The fixed harness loaded as the dynamic worker's main module. The generated
1431
+ * source becomes `program.mjs` (`export default (<expression>);`) — a module,
1432
+ * never `eval`. The harness installs namespace globals and a bounded console,
1433
+ * imports the program, invokes it exactly once, and returns one envelope the
1434
+ * host validates through Effect Schema.
1435
+ */
1436
+ const HARNESS_MODULE = String.raw`
1437
+ import { WorkerEntrypoint } from "cloudflare:workers";
1438
+ import programDefault from "./program.js";
1439
+
1440
+ const encoder = new TextEncoder();
1441
+ const utf8 = (text) => encoder.encode(text).byteLength;
1442
+ const safeText = (value) => {
1443
+ try {
1444
+ if (value instanceof Error) return (value.name + ": " + value.message).slice(0, 4000);
1445
+ if (typeof value === "string") return value.slice(0, 4000);
1446
+ const encoded = JSON.stringify(value);
1447
+ return (encoded === undefined ? String(value) : encoded).slice(0, 4000);
1448
+ } catch {
1449
+ return "[unserializable value]";
1450
+ }
1451
+ };
1452
+ const safeJson = (value) => {
1453
+ try {
1454
+ const encoded = JSON.stringify(value);
1455
+ if (encoded !== undefined && encoded.length <= 4000) return JSON.parse(encoded);
1456
+ } catch {}
1457
+ return safeText(value);
1458
+ };
1459
+
1460
+ export default class CodeModeHarness extends WorkerEntrypoint {
1461
+ async run() {
1462
+ const config = JSON.parse(this.env.CODE_MODE_PASS);
1463
+ const host = this.env.CODE_MODE_HOST;
1464
+ const limits = config.limits;
1465
+ const logs = [];
1466
+ let logBytes = 0;
1467
+ let fatal;
1468
+ const boundedLogs = () => logs.slice(0, 4096);
1469
+ const write = (...values) => {
1470
+ const joined = values.map(safeText).join(" ");
1471
+ const line = joined.length > 16000 ? joined.slice(0, 15999) + "…" : joined;
1472
+ const bytes = utf8(line);
1473
+ if (logs.length >= 4096 || logBytes + bytes > limits.maxLogBytes) {
1474
+ fatal = fatal ?? { _tag: "log-limit", observed: logBytes + bytes, logs: boundedLogs() };
1475
+ throw new Error("code-mode log limit exceeded");
1476
+ }
1477
+ logs.push(line);
1478
+ logBytes += bytes;
1479
+ };
1480
+ globalThis.console = { log: write, info: write, warn: write, error: write, debug: write };
1481
+
1482
+ let hostCalls = 0;
1483
+ const makeMethod = (namespace, method) => async (argument) => {
1484
+ hostCalls += 1;
1485
+ if (hostCalls > limits.maxHostCalls) {
1486
+ fatal = fatal ?? { _tag: "host-call-limit", logs: boundedLogs() };
1487
+ throw new Error("code-mode host-call limit exceeded");
1488
+ }
1489
+ let argText;
1490
+ try {
1491
+ argText = JSON.stringify(argument);
1492
+ } catch {}
1493
+ if (argText === undefined || utf8(argText) > limits.maxHostCallArgumentBytes) {
1494
+ fatal = fatal ?? {
1495
+ _tag: "argument-limit",
1496
+ observed: argText === undefined ? 0 : utf8(argText),
1497
+ logs: boundedLogs(),
1498
+ };
1499
+ throw new Error("code-mode host-call argument limit exceeded");
1500
+ }
1501
+ const outcome = await host.call(config.passId, {
1502
+ namespace,
1503
+ method,
1504
+ argument: JSON.parse(argText),
1505
+ });
1506
+ if (outcome !== null && typeof outcome === "object" && outcome._tag === "CodeHostCallSuccess") {
1507
+ return outcome.value;
1508
+ }
1509
+ if (outcome !== null && typeof outcome === "object" && outcome._tag === "CodeHostCallFailure") {
1510
+ throw outcome.error;
1511
+ }
1512
+ fatal = fatal ?? { _tag: "protocol", message: "host returned an unrecognized outcome" };
1513
+ throw new Error("code-mode host protocol violation");
1514
+ };
1515
+ for (const namespace of config.namespaces) {
1516
+ const methods = {};
1517
+ for (const method of namespace.methods) {
1518
+ methods[method] = makeMethod(namespace.name, method);
1519
+ }
1520
+ globalThis[namespace.name] = methods;
1521
+ }
1522
+
1523
+ // program.js is imported statically at the top of this module, so a
1524
+ // syntactically invalid program fails the whole harness at load (mapped
1525
+ // to a source error by the host). Using a static import keeps this module
1526
+ // free of dynamic-import expressions, which single-script Miniflare hosts
1527
+ // reject. The isolation boundary does NOT depend on the ordering of this
1528
+ // import versus the console/namespace shims installed below: the loaded
1529
+ // Worker has globalOutbound: null and no bindings, secrets, or env from
1530
+ // the Worker Loader config BEFORE any module in the graph evaluates, so
1531
+ // module-level program code has no ambient authority regardless. The
1532
+ // shims below are usability wrappers (bounded console, namespace globals),
1533
+ // and the accepted program is a single async-function expression whose
1534
+ // body runs only when invoked here — after the shims exist.
1535
+ const program = programDefault;
1536
+ if (typeof program !== "function") {
1537
+ return { _tag: "source-not-a-function", actual: typeof program };
1538
+ }
1539
+ try {
1540
+ const value = await program();
1541
+ if (fatal !== undefined) return fatal;
1542
+ let text;
1543
+ try {
1544
+ text = JSON.stringify(value);
1545
+ } catch {}
1546
+ if (text === undefined) {
1547
+ return {
1548
+ _tag: "program-failed",
1549
+ reason: "non-json-result",
1550
+ thrown: null,
1551
+ message: "The program must return a JSON value",
1552
+ logs: boundedLogs(),
1553
+ };
1554
+ }
1555
+ const resultBytes = utf8(text);
1556
+ if (resultBytes > limits.maxResultBytes) {
1557
+ return { _tag: "result-limit", observed: resultBytes, logs: boundedLogs() };
1558
+ }
1559
+ return {
1560
+ _tag: "completed",
1561
+ value: JSON.parse(text),
1562
+ logs: boundedLogs(),
1563
+ hostCalls,
1564
+ logBytes,
1565
+ resultBytes,
1566
+ };
1567
+ } catch (cause) {
1568
+ if (fatal !== undefined) return fatal;
1569
+ return {
1570
+ _tag: "program-failed",
1571
+ reason: cause instanceof Error ? "threw" : "rejected",
1572
+ thrown: safeJson(cause),
1573
+ message: safeText(cause),
1574
+ logs: boundedLogs(),
1575
+ };
1576
+ }
1577
+ }
1578
+ }
1579
+ `;
1580
+ const BoundedLogs = Schema.Array(Schema.String.check(Schema.isMaxLength(16 * 1024))).check(Schema.isMaxLength(4096));
1581
+ const HarnessCompleted = Schema.Struct({
1582
+ _tag: Schema.Literal("completed"),
1583
+ value: Schema.Json,
1584
+ logs: BoundedLogs,
1585
+ hostCalls: Schema.Natural,
1586
+ logBytes: Schema.Natural,
1587
+ resultBytes: Schema.Natural
1588
+ });
1589
+ const HarnessSourceInvalid = Schema.Struct({
1590
+ _tag: Schema.Literal("source-invalid"),
1591
+ message: Schema.String
1592
+ });
1593
+ const HarnessNotAFunction = Schema.Struct({
1594
+ _tag: Schema.Literal("source-not-a-function"),
1595
+ actual: Schema.String
1596
+ });
1597
+ const HarnessProgramFailed = Schema.Struct({
1598
+ _tag: Schema.Literal("program-failed"),
1599
+ reason: Schema.Literals([
1600
+ "threw",
1601
+ "rejected",
1602
+ "non-json-result"
1603
+ ]),
1604
+ thrown: Schema.Json,
1605
+ message: Schema.String,
1606
+ logs: BoundedLogs
1607
+ });
1608
+ const HarnessLogLimit = Schema.Struct({
1609
+ _tag: Schema.Literal("log-limit"),
1610
+ observed: Schema.Natural,
1611
+ logs: BoundedLogs
1612
+ });
1613
+ const HarnessArgumentLimit = Schema.Struct({
1614
+ _tag: Schema.Literal("argument-limit"),
1615
+ observed: Schema.Natural,
1616
+ logs: BoundedLogs
1617
+ });
1618
+ const HarnessResultLimit = Schema.Struct({
1619
+ _tag: Schema.Literal("result-limit"),
1620
+ observed: Schema.Natural,
1621
+ logs: BoundedLogs
1622
+ });
1623
+ const HarnessHostCallLimit = Schema.Struct({
1624
+ _tag: Schema.Literal("host-call-limit"),
1625
+ logs: BoundedLogs
1626
+ });
1627
+ const HarnessProtocol = Schema.Struct({
1628
+ _tag: Schema.Literal("protocol"),
1629
+ message: Schema.String
1630
+ });
1631
+ const HarnessOutcome = Schema.Union([
1632
+ HarnessCompleted,
1633
+ HarnessSourceInvalid,
1634
+ HarnessNotAFunction,
1635
+ HarnessProgramFailed,
1636
+ HarnessLogLimit,
1637
+ HarnessArgumentLimit,
1638
+ HarnessResultLimit,
1639
+ HarnessHostCallLimit,
1640
+ HarnessProtocol
1641
+ ]);
1642
+ const decodeHarnessOutcome = (value) => {
1643
+ try {
1644
+ return Schema.decodeUnknownOption(HarnessOutcome)(value);
1645
+ } catch {
1646
+ return Option.none();
1647
+ }
1648
+ };
1649
+ const decodeHostCall = (value) => {
1650
+ try {
1651
+ return Schema.decodeUnknownOption(CodeHostCall)(value);
1652
+ } catch {
1653
+ return Option.none();
1654
+ }
1655
+ };
1656
+ /**
1657
+ * Project a host outcome to the plain JSON envelope the harness reads. A
1658
+ * `CodeExecutionHost` may return either real `CodeHostCallResult` instances
1659
+ * (the substitute and conformance kit) or plain-object equivalents (the Code
1660
+ * Mode capability's broker route), so this reads the shared fields rather than
1661
+ * `Schema.encodeSync`, which would reject a plain object.
1662
+ */
1663
+ const hostResultEnvelope = (outcome) => outcome._tag === "CodeHostCallSuccess" ? {
1664
+ _tag: "CodeHostCallSuccess",
1665
+ value: outcome.value
1666
+ } : {
1667
+ _tag: "CodeHostCallFailure",
1668
+ error: outcome.error
1669
+ };
1670
+ const utf8ByteLength = (value) => {
1671
+ let total = 0;
1672
+ for (const character of value) {
1673
+ const codePoint = character.codePointAt(0) ?? 0;
1674
+ total += codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
1675
+ }
1676
+ return total;
1677
+ };
1678
+ const encodedJsonByteLength = (value) => {
1679
+ try {
1680
+ const encoded = JSON.stringify(value);
1681
+ return encoded === void 0 ? void 0 : utf8ByteLength(encoded);
1682
+ } catch {
1683
+ return;
1684
+ }
1685
+ };
1686
+ /** Reserved global names the harness owns inside the dynamic worker. */
1687
+ const reservedHarnessGlobals = /* @__PURE__ */ new Set(["console"]);
1688
+ const passCounterState = { next: 0 };
1689
+ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(function* (request) {
1690
+ if (request.network._tag !== "NetworkDisabled") return yield* CodeExecutorUnsupportedError.make({
1691
+ implementation: dynamicWorkerImplementation,
1692
+ feature: "network",
1693
+ message: "The Dynamic Worker executor denies all egress with globalOutbound: null; an allowlist is not supported in the first slice"
1694
+ });
1695
+ const sourceBytes = utf8ByteLength(request.source);
1696
+ if (sourceBytes > request.limits.maxSourceBytes) return yield* CodeSourceError.make({
1697
+ implementation: dynamicWorkerImplementation,
1698
+ reason: "oversized",
1699
+ message: `Source is ${sourceBytes} bytes; the request allows ${request.limits.maxSourceBytes}`
1700
+ });
1701
+ for (const namespace of request.namespaces) if (reservedHarnessGlobals.has(namespace.name)) return yield* CodeExecutorUnsupportedError.make({
1702
+ implementation: dynamicWorkerImplementation,
1703
+ feature: "namespaces",
1704
+ message: `Namespace ${namespace.name} collides with a harness binding`
1705
+ });
1706
+ const host = yield* CodeExecutionHost;
1707
+ passCounterState.next += 1;
1708
+ const passId = `code-mode-pass-${passCounterState.next}-${crypto.randomUUID()}`;
1709
+ const pending = [];
1710
+ let wake;
1711
+ let issuedHostCalls = 0;
1712
+ const dispatch = (hostCall) => new Promise((resolve, reject) => {
1713
+ issuedHostCalls += 1;
1714
+ if (issuedHostCalls > request.limits.maxHostCalls + 1) {
1715
+ reject(/* @__PURE__ */ new Error("host-call limit exceeded"));
1716
+ return;
1717
+ }
1718
+ pending.push({
1719
+ hostCall,
1720
+ resolve,
1721
+ reject
1722
+ });
1723
+ wake?.();
1724
+ });
1725
+ yield* Effect.acquireRelease(Effect.sync(() => {
1726
+ passRegistry.set(passId, { dispatch });
1727
+ }), () => Effect.sync(() => {
1728
+ passRegistry.delete(passId);
1729
+ }));
1730
+ const nextPending = Effect.suspend(() => {
1731
+ const item = pending.shift();
1732
+ if (item !== void 0) return Effect.succeed(item);
1733
+ return Effect.callback((resume) => {
1734
+ wake = () => {
1735
+ wake = void 0;
1736
+ const next = pending.shift();
1737
+ if (next !== void 0) resume(Effect.succeed(next));
1738
+ };
1739
+ });
1740
+ });
1741
+ const serveHostCalls = Effect.gen(function* () {
1742
+ let served = 0;
1743
+ while (true) {
1744
+ const item = yield* nextPending;
1745
+ served += 1;
1746
+ if (served > request.limits.maxHostCalls) return yield* CodeHostCallLimitError.make({
1747
+ implementation: dynamicWorkerImplementation,
1748
+ limit: request.limits.maxHostCalls,
1749
+ logs: []
1750
+ });
1751
+ const decoded = decodeHostCall(item.hostCall);
1752
+ if (Option.isNone(decoded)) {
1753
+ item.reject(/* @__PURE__ */ new TypeError("host calls must match the CodeHostCall schema"));
1754
+ continue;
1755
+ }
1756
+ const outcome = yield* host.call(decoded.value);
1757
+ if (outcome._tag === "CodeHostCallSuccess") {
1758
+ const bytes = encodedJsonByteLength(outcome.value);
1759
+ if (bytes === void 0 || bytes > request.limits.maxHostCallResultBytes) return yield* CodeOutputLimitError.make({
1760
+ implementation: dynamicWorkerImplementation,
1761
+ surface: "host-call-result",
1762
+ limit: request.limits.maxHostCallResultBytes,
1763
+ observed: bytes ?? 0,
1764
+ logs: []
1765
+ });
1766
+ }
1767
+ item.resolve(hostResultEnvelope(outcome));
1768
+ }
1769
+ });
1770
+ const workerCode = {
1771
+ compatibilityDate: options.compatibilityDate ?? "2025-05-01",
1772
+ allowExperimental: true,
1773
+ mainModule: "harness.js",
1774
+ modules: {
1775
+ "harness.js": HARNESS_MODULE,
1776
+ "program.js": `export default (\n${request.source}\n);`
1777
+ },
1778
+ env: {
1779
+ CODE_MODE_HOST: options.hostStub,
1780
+ CODE_MODE_PASS: JSON.stringify({
1781
+ passId,
1782
+ namespaces: request.namespaces.map((namespace) => ({
1783
+ name: namespace.name,
1784
+ methods: namespace.methods
1785
+ })),
1786
+ limits: {
1787
+ maxLogBytes: request.limits.maxLogBytes,
1788
+ maxResultBytes: request.limits.maxResultBytes,
1789
+ maxHostCalls: request.limits.maxHostCalls,
1790
+ maxHostCallArgumentBytes: request.limits.maxHostCallArgumentBytes
1791
+ }
1792
+ })
1793
+ },
1794
+ globalOutbound: null,
1795
+ ...request.limits.cpuMillis === void 0 ? {} : { limits: {
1796
+ cpuMs: request.limits.cpuMillis,
1797
+ subRequests: request.limits.maxHostCalls + 8
1798
+ } }
1799
+ };
1800
+ const startedAt = yield* Clock.currentTimeMillis;
1801
+ const worker = yield* Effect.acquireRelease(Effect.try({
1802
+ try: () => options.loader.load(workerCode),
1803
+ catch: (cause) => {
1804
+ const text = cause instanceof Error ? cause.message : String(cause);
1805
+ if (/syntaxerror|failed to (compile|parse)/i.test(text)) return CodeSourceError.make({
1806
+ implementation: dynamicWorkerImplementation,
1807
+ reason: "invalid",
1808
+ message: text.slice(0, 8e3)
1809
+ });
1810
+ return CodeExecutorStartError.make({
1811
+ implementation: dynamicWorkerImplementation,
1812
+ message: `The Worker Loader rejected the pass: ${text}`.slice(0, 8e3),
1813
+ cause
1814
+ });
1815
+ }
1816
+ }), (stub) => Effect.sync(() => {
1817
+ stub[Symbol.dispose]?.();
1818
+ }));
1819
+ const server = yield* serveHostCalls.pipe(Effect.forkScoped);
1820
+ const rpc = Effect.tryPromise({
1821
+ try: async () => {
1822
+ return await worker.getEntrypoint().run();
1823
+ },
1824
+ catch: (cause) => classifyWorkerFailure(cause, request.limits.maxWallTime)
1825
+ });
1826
+ const raw = yield* Effect.raceFirst(rpc, Fiber.join(server)).pipe(Effect.timeoutOrElse({
1827
+ duration: request.limits.maxWallTime,
1828
+ orElse: () => CodeExecutionTimeoutError.make({
1829
+ implementation: dynamicWorkerImplementation,
1830
+ kind: "wall-clock",
1831
+ maxWallTime: request.limits.maxWallTime,
1832
+ logs: []
1833
+ })
1834
+ }), Effect.ensuring(Fiber.interrupt(server)));
1835
+ const finishedAt = yield* Clock.currentTimeMillis;
1836
+ const outcome = decodeHarnessOutcome(raw);
1837
+ if (Option.isNone(outcome)) return yield* CodeExecutionProtocolError.make({
1838
+ implementation: dynamicWorkerImplementation,
1839
+ message: "The dynamic worker returned a value outside the harness envelope schema"
1840
+ });
1841
+ switch (outcome.value._tag) {
1842
+ case "completed": return CodeExecutionResult.make({
1843
+ implementation: dynamicWorkerImplementation,
1844
+ value: outcome.value.value,
1845
+ logs: outcome.value.logs,
1846
+ resourceUse: CodeExecutionResourceUse.make({
1847
+ wallTime: Duration.millis(Math.max(0, finishedAt - startedAt)),
1848
+ hostCalls: outcome.value.hostCalls,
1849
+ logBytes: outcome.value.logBytes,
1850
+ resultBytes: outcome.value.resultBytes
1851
+ })
1852
+ });
1853
+ case "source-invalid": return yield* CodeSourceError.make({
1854
+ implementation: dynamicWorkerImplementation,
1855
+ reason: "invalid",
1856
+ message: outcome.value.message.slice(0, 8e3)
1857
+ });
1858
+ case "source-not-a-function": return yield* CodeSourceError.make({
1859
+ implementation: dynamicWorkerImplementation,
1860
+ reason: "not-a-function",
1861
+ message: `The source expression evaluated to ${outcome.value.actual}; it must evaluate to one async function`
1862
+ });
1863
+ case "program-failed": return yield* CodeProgramFailedError.make({
1864
+ implementation: dynamicWorkerImplementation,
1865
+ reason: outcome.value.reason,
1866
+ thrown: outcome.value.thrown,
1867
+ message: outcome.value.message.slice(0, 8e3),
1868
+ logs: outcome.value.logs
1869
+ });
1870
+ case "log-limit": return yield* CodeOutputLimitError.make({
1871
+ implementation: dynamicWorkerImplementation,
1872
+ surface: "logs",
1873
+ limit: request.limits.maxLogBytes,
1874
+ observed: outcome.value.observed,
1875
+ logs: outcome.value.logs
1876
+ });
1877
+ case "argument-limit": return yield* CodeOutputLimitError.make({
1878
+ implementation: dynamicWorkerImplementation,
1879
+ surface: "host-call-argument",
1880
+ limit: request.limits.maxHostCallArgumentBytes,
1881
+ observed: outcome.value.observed,
1882
+ logs: outcome.value.logs
1883
+ });
1884
+ case "result-limit": return yield* CodeOutputLimitError.make({
1885
+ implementation: dynamicWorkerImplementation,
1886
+ surface: "result",
1887
+ limit: request.limits.maxResultBytes,
1888
+ observed: outcome.value.observed,
1889
+ logs: outcome.value.logs
1890
+ });
1891
+ case "host-call-limit": return yield* CodeHostCallLimitError.make({
1892
+ implementation: dynamicWorkerImplementation,
1893
+ limit: request.limits.maxHostCalls,
1894
+ logs: outcome.value.logs
1895
+ });
1896
+ case "protocol": return yield* CodeExecutionProtocolError.make({
1897
+ implementation: dynamicWorkerImplementation,
1898
+ message: outcome.value.message.slice(0, 8e3)
1899
+ });
1900
+ }
1901
+ });
1902
+ /**
1903
+ * Expected worker-level failures map into the typed union with bounded
1904
+ * diagnostics; anything unrecognized stays a start/termination error rather
1905
+ * than a fabricated program result.
1906
+ */
1907
+ const classifyWorkerFailure = (cause, maxWallTime) => {
1908
+ const text = (() => {
1909
+ try {
1910
+ return cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
1911
+ } catch {
1912
+ return "[unserializable worker failure]";
1913
+ }
1914
+ })();
1915
+ if (/syntaxerror|failed to (compile|parse)/i.test(text)) return CodeSourceError.make({
1916
+ implementation: dynamicWorkerImplementation,
1917
+ reason: "invalid",
1918
+ message: text.slice(0, 8e3)
1919
+ });
1920
+ if (/cpu/i.test(text)) return CodeExecutionTimeoutError.make({
1921
+ implementation: dynamicWorkerImplementation,
1922
+ kind: "cpu",
1923
+ maxWallTime,
1924
+ logs: []
1925
+ });
1926
+ if (/failed to start worker/i.test(text)) return CodeExecutorStartError.make({
1927
+ implementation: dynamicWorkerImplementation,
1928
+ message: text.slice(0, 8e3),
1929
+ cause
1930
+ });
1931
+ return CodeExecutorTerminatedError.make({
1932
+ implementation: dynamicWorkerImplementation,
1933
+ message: text.slice(0, 8e3)
1934
+ });
1935
+ };
1936
+ /** Layer building the Dynamic Worker `CodeExecutor` from resolved bindings. */
1937
+ const dynamicWorkerCodeExecutorLayer = (options) => Layer.succeed(CodeExecutor)(CodeExecutor.of({ execute: makeExecute(options) }));
1938
+ //#endregion
1939
+ export { AbortRecorded, AdminExplainRequest, AdminFailed, AdminFailure, AdminResponse, AdminVerifyRequest, AdmissionLimitExceeded, ApprovalRecorded, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CloudflareAdmissionLimitsValue, CloudflareBindingError, CloudflareConversationClient, CloudflareDurableRuntime, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError, CloudflareRuntimeTelemetry, CloudflareTelemetryExportError, CodeModeHostEntrypoint, ConversationClientError, ConversationMaintenance, ConversationObjectIdentity, ConversationObjectNamespace, ConversationObjectPorts, DEFAULT_MAX_DATABASE_BYTES, DurableAlarmError, DurableAlarmService, DurableObjectContext, ExplainedRecovery, HostFailed, HostFailure, HostProtocolError, HostResponse, MaintenancePassReport, ObligationsScanned, ObservePageRequest, ObservedPage, RetryExecuted, SettlementReached, SubmitRequest, SubmitSucceeded, UnknownResolutionRecorded, VerifiedIntegrity, boundHostDiagnostic, cloudflareWakeSchedulerLayer, conversationNamespaceFromEnv, conversationNamespaceLayer, conversationPortTransportLayer, decodeAbortCommand, decodeAdminExplainRequest, decodeAdminResponse, decodeAdminVerifyRequest, decodeApprovalDecisionCommand, decodeHostResponse, decodeObligationThresholds, decodeObservePageRequest, decodeReceipt, decodeRetryCommand, decodeSubmitRequest, decodeUnknownResolutionCommand, dynamicWorkerCodeExecutorLayer, dynamicWorkerImplementation, encodeAbortCommand, encodeAdminResponse, encodeApprovalDecisionCommand, encodeHostResponse, encodeObservePageRequest, encodeReceipt, encodeSubmitRequest, encodeUnknownResolutionCommand, makeConversationObjectClass };
1077
1940
 
1078
1941
  //# sourceMappingURL=index.mjs.map