@effect-agent/platform-cloudflare 0.1.0-beta.24 → 0.1.0-beta.26
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.d.mts +43 -60
- package/dist/index.mjs +259 -216
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/alarm.ts +2 -1
- package/src/bindings.ts +31 -30
- package/src/boundary.ts +51 -0
- package/src/client.ts +86 -187
- package/src/code-mode-executor.ts +130 -115
- package/src/conversation-object.ts +46 -20
- package/src/layers.ts +17 -33
- package/src/wake-scheduler.ts +2 -1
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Clock, Context, Crypto, Deferred, Duration, Effect, Exit, Fiber, Layer, Option, PubSub, Queue, Random, Ref, Schema, Semaphore, Stream } from "effect";
|
|
1
|
+
import { Clock, Context, Crypto, Deferred, Duration, Effect, Exit, Fiber, Layer, Option, Predicate, PubSub, Queue, Random, Ref, Schema, Semaphore, 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, makeWakeSubscriptionHub, operationAuthorizerLayer } from "@effect-agent/session";
|
|
3
|
-
import { ConversationPortTransport, DEFAULT_MAX_STORED_VALUE_BYTES, conversationStoreLayer,
|
|
3
|
+
import { ConversationPortTransport, DEFAULT_MAX_STORED_VALUE_BYTES, conversationStoreLayer, decodePortRequest, encodePortResponse, executePortRequest, 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 { RunContextPreparationPassthrough } from "@effect-agent/engine";
|
|
6
6
|
import { BrowserCrypto } from "@effect/platform-browser";
|
|
7
7
|
import { SqliteClient } from "@effect/sql-sqlite-do";
|
|
8
8
|
import { DurableObject, DurableObjectState, WorkerEnvironment } from "effect-cf";
|
|
9
9
|
import { CodeExecutionHost, CodeExecutionProtocolError, CodeExecutionResourceUse, CodeExecutionResult, CodeExecutionTimeoutError, CodeExecutor, CodeExecutorStartError, CodeExecutorTerminatedError, CodeExecutorUnsupportedError, CodeHostCall, CodeHostCallLimitError, CodeHostCallResult, CodeOutputLimitError, CodeProgramFailedError, CodeSourceError, SandboxImplementation } from "@effect-agent/sandbox";
|
|
10
|
-
import {
|
|
10
|
+
import { RpcTarget } from "cloudflare:workers";
|
|
11
11
|
//#region src/bindings.ts
|
|
12
12
|
/**
|
|
13
13
|
* Cloudflare platform bindings as Effect services (DEPLOY-010: "Cloudflare platform bindings
|
|
@@ -36,17 +36,29 @@ var ConversationObjectNamespace = class ConversationObjectNamespace extends Cont
|
|
|
36
36
|
* narrowest-boundary check (structural probe for the namespace surface the transport uses);
|
|
37
37
|
* a missing or misshaped binding fails typed before any Layer is built.
|
|
38
38
|
*/
|
|
39
|
-
const conversationNamespaceFromEnv = (env, binding)
|
|
40
|
-
if (
|
|
39
|
+
const conversationNamespaceFromEnv = Effect.fn("conversationNamespaceFromEnv")(function* (env, binding) {
|
|
40
|
+
if (!Predicate.isObjectKeyword(env)) return yield* CloudflareBindingError.make({
|
|
41
41
|
binding,
|
|
42
42
|
message: "The Worker environment is not an object; no bindings are available."
|
|
43
|
-
})
|
|
44
|
-
const candidate =
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
});
|
|
44
|
+
const candidate = yield* Effect.try({
|
|
45
|
+
try: () => {
|
|
46
|
+
const value = Reflect.get(env, binding);
|
|
47
|
+
if (!Predicate.isObjectKeyword(value)) return void 0;
|
|
48
|
+
const idFromName = Reflect.get(value, "idFromName");
|
|
49
|
+
const get = Reflect.get(value, "get");
|
|
50
|
+
return typeof idFromName === "function" && typeof get === "function" ? value : void 0;
|
|
51
|
+
},
|
|
52
|
+
catch: () => CloudflareBindingError.make({
|
|
53
|
+
binding,
|
|
54
|
+
message: `env.${binding} could not be inspected as a DurableObjectNamespace binding.`
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
if (candidate !== void 0) return candidate;
|
|
58
|
+
return yield* CloudflareBindingError.make({
|
|
47
59
|
binding,
|
|
48
60
|
message: `env.${binding} is not a DurableObjectNamespace binding; declare the Conversation Object class under this binding in the Worker configuration.`
|
|
49
|
-
})
|
|
61
|
+
});
|
|
50
62
|
});
|
|
51
63
|
/** `ConversationObjectNamespace` built from the untyped Worker `env` (fails typed). */
|
|
52
64
|
const conversationNamespaceLayer = (env, binding) => Layer.effect(ConversationObjectNamespace)(Effect.map(conversationNamespaceFromEnv(env, binding), (namespace) => ({ namespace })));
|
|
@@ -174,6 +186,44 @@ const CLOUDFLARE_RUNTIME_DEFAULTS = {
|
|
|
174
186
|
maxDatabaseBytes: DEFAULT_MAX_DATABASE_BYTES
|
|
175
187
|
};
|
|
176
188
|
//#endregion
|
|
189
|
+
//#region src/boundary.ts
|
|
190
|
+
const MAX_FOREIGN_DIAGNOSTIC_LENGTH = 8192;
|
|
191
|
+
const boundForeignDiagnostic = (message) => message.slice(0, MAX_FOREIGN_DIAGNOSTIC_LENGTH);
|
|
192
|
+
/** Render a foreign failure without trusting accessors or coercion hooks on the value. */
|
|
193
|
+
const safeCauseMessage = (cause, fallback) => {
|
|
194
|
+
try {
|
|
195
|
+
const message = cause instanceof Error ? cause.message : cause;
|
|
196
|
+
return boundForeignDiagnostic(typeof message === "string" ? message : String(message));
|
|
197
|
+
} catch {
|
|
198
|
+
return boundForeignDiagnostic(fallback);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
/** Include an Error name when worker-failure classification needs it. */
|
|
202
|
+
const safeCauseDiagnostic = (cause, fallback) => {
|
|
203
|
+
try {
|
|
204
|
+
return cause instanceof Error ? boundForeignDiagnostic(`${cause.name}: ${cause.message}`) : safeCauseMessage(cause, fallback);
|
|
205
|
+
} catch {
|
|
206
|
+
return boundForeignDiagnostic(fallback);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
/** Read Cloudflare RPC classifications without letting a hostile proxy defect the client. */
|
|
210
|
+
const cloudflareFailureSignals = (cause) => {
|
|
211
|
+
if (!Predicate.isObjectKeyword(cause)) return {};
|
|
212
|
+
try {
|
|
213
|
+
const retryableValue = Reflect.get(cause, "retryable");
|
|
214
|
+
const overloadedValue = Reflect.get(cause, "overloaded");
|
|
215
|
+
const resetValue = Reflect.get(cause, "durableObjectReset");
|
|
216
|
+
const retryable = typeof retryableValue === "boolean" ? retryableValue : resetValue === true ? true : void 0;
|
|
217
|
+
const overloaded = typeof overloadedValue === "boolean" ? overloadedValue : void 0;
|
|
218
|
+
return {
|
|
219
|
+
...retryable === void 0 ? {} : { retryable },
|
|
220
|
+
...overloaded === void 0 ? {} : { overloaded }
|
|
221
|
+
};
|
|
222
|
+
} catch {
|
|
223
|
+
return {};
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
//#endregion
|
|
177
227
|
//#region src/alarm.ts
|
|
178
228
|
/**
|
|
179
229
|
* The single multiplexed Durable Object alarm (decision D-P6-2). A Durable Object has ONE
|
|
@@ -194,7 +244,7 @@ var DurableAlarmError = class extends Schema.TaggedError()("DurableAlarmError",
|
|
|
194
244
|
}) {};
|
|
195
245
|
const alarmFailure = (operation) => (cause) => DurableAlarmError.make({
|
|
196
246
|
operation,
|
|
197
|
-
message: cause
|
|
247
|
+
message: safeCauseMessage(cause, "The Cloudflare alarm API failed without a diagnostic"),
|
|
198
248
|
cause
|
|
199
249
|
});
|
|
200
250
|
/** `ctx.storage` alarm slot as an Effect service; storage is truth, never a memory field. */
|
|
@@ -505,7 +555,7 @@ const cloudflareWakeSchedulerLayer = Layer.effect(WakeScheduler)(Effect.gen(func
|
|
|
505
555
|
try: () => namespace.get(namespace.idFromName(conversationId)).wake(),
|
|
506
556
|
catch: (cause) => RemoteWakeDropped.make({
|
|
507
557
|
conversationId,
|
|
508
|
-
message: cause
|
|
558
|
+
message: safeCauseMessage(cause, "The remote wake failed without a diagnostic"),
|
|
509
559
|
cause
|
|
510
560
|
})
|
|
511
561
|
}).pipe(Effect.catch((error) => Effect.logWarning(`CloudflareWakeScheduler: remote wake of ${conversationId} dropped`, error)), Effect.asVoid);
|
|
@@ -605,10 +655,10 @@ const conversationPortTransportLayer = Layer.effect(ConversationPortTransport)(E
|
|
|
605
655
|
//#endregion
|
|
606
656
|
//#region src/layers.ts
|
|
607
657
|
/**
|
|
608
|
-
* Owner-side
|
|
609
|
-
*
|
|
610
|
-
* request cannot bounce between Objects — and
|
|
611
|
-
*
|
|
658
|
+
* Owner-side execution port for a `portCall` request the wire endpoint has already decoded.
|
|
659
|
+
* It executes against THIS Object's LOCAL port facets — never the routed decorators, so a
|
|
660
|
+
* request cannot bounce between Objects — and returns the typed response for the endpoint to
|
|
661
|
+
* encode.
|
|
612
662
|
*/
|
|
613
663
|
var ConversationObjectPorts = class extends Context.Service()("@effect-agent/platform-cloudflare/ConversationObjectPorts") {};
|
|
614
664
|
const decodeConfigValue = Schema.decodeUnknownEffect(CloudflareDurableRuntimeConfigValue);
|
|
@@ -645,10 +695,7 @@ const conversationIdFromState = (ctx) => ctx.id.name === void 0 ? Effect.fail(Cl
|
|
|
645
695
|
message: `The Durable Object name is not a valid ConversationId: ${error.message}`,
|
|
646
696
|
cause: error
|
|
647
697
|
})));
|
|
648
|
-
const resolveBindings = (source, context) => source === void 0 ? Effect.succeed([]) : Effect.
|
|
649
|
-
const bindings = source(context);
|
|
650
|
-
return Effect.isEffect(bindings) ? bindings : Effect.succeed(bindings);
|
|
651
|
-
}) : Effect.succeed(source);
|
|
698
|
+
const resolveBindings = (source, context) => source === void 0 ? Effect.succeed([]) : Effect.suspend(() => source(context));
|
|
652
699
|
const resolveRunContext = (source, context) => typeof source === "function" ? source(context) : source;
|
|
653
700
|
/**
|
|
654
701
|
* The DC Layer assembly (deployment §12: a Layer-assembly library, not an app entrypoint;
|
|
@@ -699,7 +746,7 @@ var CloudflareDurableRuntime = class {
|
|
|
699
746
|
const localPorts = Layer.mergeAll(conversationStoreLayer, submissionLedgerLayer).pipe(Layer.provide(infrastructure));
|
|
700
747
|
const portsEndpointLayer = Layer.effect(ConversationObjectPorts)(Effect.gen(function* () {
|
|
701
748
|
const local = yield* Effect.context();
|
|
702
|
-
return ConversationObjectPorts.of({ handle: (
|
|
749
|
+
return ConversationObjectPorts.of({ handle: (request) => executePortRequest(request).pipe(Effect.provide(local)) });
|
|
703
750
|
})).pipe(Layer.provide(localPorts));
|
|
704
751
|
const routedPorts = Layer.mergeAll(routedSubmissionLedgerLayer({ localConversationId: conversationId }), routedConversationStoreLayer({ localConversationId: conversationId })).pipe(Layer.provide(localPorts), Layer.provide(conversationPortTransportLayer));
|
|
705
752
|
const runtimeConfigLayer = Layer.succeed(DurableRuntimeConfig)(DurableRuntimeConfig.make({
|
|
@@ -852,107 +899,88 @@ const decodeUnknownResolutionCommand = Schema.decodeUnknownEffect(UnknownResolut
|
|
|
852
899
|
const encodeUnknownResolutionCommand = Schema.encodeEffect(UnknownResolutionCommand);
|
|
853
900
|
const encodeHostResponse = Schema.encodeEffect(HostResponse);
|
|
854
901
|
const decodeHostResponse = Schema.decodeUnknownEffect(HostResponse);
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
902
|
+
/** Failure surface of `CloudflareConversationClient.submit`. */
|
|
903
|
+
const ClientSubmitHostFailure = Schema.Union([
|
|
904
|
+
AgentInputError,
|
|
905
|
+
DigestError,
|
|
906
|
+
AdmissionConflict,
|
|
907
|
+
LedgerError,
|
|
908
|
+
ConversationStoreError,
|
|
909
|
+
ConversationNotMaterialized,
|
|
910
|
+
AppendConflict,
|
|
911
|
+
FenceRejected,
|
|
912
|
+
DurableRuntimeFailpointError,
|
|
913
|
+
AdmissionLimitExceeded,
|
|
914
|
+
DurableAlarmError,
|
|
915
|
+
HostProtocolError
|
|
868
916
|
]);
|
|
869
|
-
const
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
917
|
+
const ClientAwaitHostFailure = Schema.Union([
|
|
918
|
+
LedgerError,
|
|
919
|
+
SettlementConflict,
|
|
920
|
+
HostProtocolError
|
|
873
921
|
]);
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
922
|
+
const ClientObserveHostFailure = Schema.Union([
|
|
923
|
+
ConversationStoreError,
|
|
924
|
+
ConversationNotMaterialized,
|
|
925
|
+
OperationDenied,
|
|
926
|
+
HostProtocolError
|
|
879
927
|
]);
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
"HostProtocolError"
|
|
928
|
+
const ClientAbortHostFailure = Schema.Union([
|
|
929
|
+
LedgerError,
|
|
930
|
+
SettlementConflict,
|
|
931
|
+
JoinedToHost,
|
|
932
|
+
DurableRuntimeFailpointError,
|
|
933
|
+
DurableAlarmError,
|
|
934
|
+
HostProtocolError
|
|
888
935
|
]);
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
936
|
+
const ClientApprovalHostFailure = Schema.Union([
|
|
937
|
+
LedgerError,
|
|
938
|
+
SettlementConflict,
|
|
939
|
+
ApprovalConflict,
|
|
940
|
+
OperationDenied,
|
|
941
|
+
DurableAlarmError,
|
|
942
|
+
HostProtocolError
|
|
896
943
|
]);
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
944
|
+
const ClientUnknownHostFailure = Schema.Union([
|
|
945
|
+
LedgerError,
|
|
946
|
+
SettlementConflict,
|
|
947
|
+
UnknownResolutionConflict,
|
|
948
|
+
JoinedToHost,
|
|
949
|
+
DurableRuntimeFailpointError,
|
|
950
|
+
OperationDenied,
|
|
951
|
+
DurableAlarmError,
|
|
952
|
+
HostProtocolError
|
|
906
953
|
]);
|
|
907
954
|
const outOfContract = (conversationId, operation, observed) => ConversationClientError.make({
|
|
908
955
|
conversationId,
|
|
909
956
|
message: boundHostDiagnostic(`The Conversation Object answered ${operation} with the out-of-contract ${observed}.`)
|
|
910
957
|
});
|
|
911
|
-
/**
|
|
912
|
-
* Narrow one decoded `HostFailed.failure` to the operation's declared failure family; an
|
|
913
|
-
* out-of-contract tag folds into `ConversationClientError` instead of being erased or
|
|
914
|
-
* re-thrown raw (the WP2 discipline). The predicate is the single documented narrowing over
|
|
915
|
-
* the closed `HostFailure` union.
|
|
916
|
-
*/
|
|
917
|
-
const narrowFailure = (tags) => (failure) => tags.has(failure._tag);
|
|
918
958
|
/** Worker-side client over the Conversation Object namespace (DEPLOY-010). */
|
|
919
959
|
var CloudflareConversationClient = class CloudflareConversationClient extends Context.Service()("@effect-agent/platform-cloudflare/CloudflareConversationClient") {
|
|
920
960
|
static layer = Layer.effect(CloudflareConversationClient)(Effect.gen(function* () {
|
|
921
961
|
const { namespace } = yield* ConversationObjectNamespace;
|
|
922
962
|
const crypto = yield* Crypto.Crypto;
|
|
923
|
-
const platformSignals = (cause) => {
|
|
924
|
-
let retryable;
|
|
925
|
-
let overloaded;
|
|
926
|
-
if (typeof cause === "object" && cause !== null) {
|
|
927
|
-
if ("retryable" in cause && typeof cause.retryable === "boolean") retryable = cause.retryable;
|
|
928
|
-
if ("overloaded" in cause && typeof cause.overloaded === "boolean") overloaded = cause.overloaded;
|
|
929
|
-
if (retryable === void 0 && "durableObjectReset" in cause && cause.durableObjectReset === true) retryable = true;
|
|
930
|
-
}
|
|
931
|
-
return {
|
|
932
|
-
...retryable === void 0 ? {} : { retryable },
|
|
933
|
-
...overloaded === void 0 ? {} : { overloaded }
|
|
934
|
-
};
|
|
935
|
-
};
|
|
936
963
|
const call = (conversationId, operation, invoke) => Effect.tryPromise({
|
|
937
964
|
try: () => invoke(namespace.get(namespace.idFromName(conversationId))),
|
|
938
965
|
catch: (cause) => ConversationClientError.make({
|
|
939
966
|
conversationId,
|
|
940
|
-
message: boundHostDiagnostic(`${operation} did not reach the Conversation Object: ${cause
|
|
967
|
+
message: boundHostDiagnostic(`${operation} did not reach the Conversation Object: ${safeCauseMessage(cause, "the RPC failed without a diagnostic")}`),
|
|
941
968
|
cause,
|
|
942
|
-
...
|
|
969
|
+
...cloudflareFailureSignals(cause)
|
|
943
970
|
})
|
|
944
971
|
}).pipe(Effect.flatMap((raw) => decodeHostResponse(raw).pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`The ${operation} answer could not be decoded: ${error.message}`) })))), Effect.withSpan("CloudflareConversationClient.call", { attributes: {
|
|
945
972
|
conversationId,
|
|
946
973
|
operation
|
|
947
974
|
} }));
|
|
948
|
-
const expect = (conversationId, operation,
|
|
949
|
-
const
|
|
975
|
+
const expect = (conversationId, operation, resultSchema, failureSchema) => {
|
|
976
|
+
const isExpectedResult = Schema.is(resultSchema);
|
|
977
|
+
const isExpectedFailure = Schema.is(failureSchema);
|
|
950
978
|
return (response) => {
|
|
951
979
|
if (response._tag === "HostFailed") {
|
|
952
980
|
const failure = response.failure;
|
|
953
|
-
return
|
|
981
|
+
return isExpectedFailure(failure) ? Effect.fail(failure) : Effect.fail(outOfContract(conversationId, operation, `failure ${failure._tag}`));
|
|
954
982
|
}
|
|
955
|
-
if (response
|
|
983
|
+
if (!isExpectedResult(response)) return Effect.fail(outOfContract(conversationId, operation, `result ${response._tag}`));
|
|
956
984
|
return Effect.succeed(response);
|
|
957
985
|
};
|
|
958
986
|
};
|
|
@@ -963,7 +991,7 @@ var CloudflareConversationClient = class CloudflareConversationClient extends Co
|
|
|
963
991
|
});
|
|
964
992
|
const encoded = yield* encodeObservePageRequest(request).pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`observePage request encode failed: ${error.message}`) })));
|
|
965
993
|
const response = yield* call(conversationId, "observePage", (stub) => stub.observePage(encoded));
|
|
966
|
-
return (yield* expect(conversationId, "observePage",
|
|
994
|
+
return (yield* expect(conversationId, "observePage", ObservedPage, ClientObserveHostFailure)(response)).records;
|
|
967
995
|
});
|
|
968
996
|
const cancelProgress = (conversationId, waiterId) => encodeCancelProgressRequest(CancelProgressRequest.make({ waiterId })).pipe(Effect.mapError(() => void 0), Effect.flatMap((encoded) => call(conversationId, "cancelProgress", (stub) => stub.cancelProgressEncoded(encoded))), Effect.asVoid, Effect.ignore);
|
|
969
997
|
return CloudflareConversationClient.of({
|
|
@@ -979,12 +1007,12 @@ var CloudflareConversationClient = class CloudflareConversationClient extends Co
|
|
|
979
1007
|
});
|
|
980
1008
|
const encoded = yield* encodeSubmitRequest(request).pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`submit request encode failed: ${error.message}`) })));
|
|
981
1009
|
const response = yield* call(options.conversationId, "submit", (stub) => stub.submitEncoded(encoded));
|
|
982
|
-
return (yield* expect(options.conversationId, "submit",
|
|
1010
|
+
return (yield* expect(options.conversationId, "submit", SubmitSucceeded, ClientSubmitHostFailure)(response)).receipt;
|
|
983
1011
|
}),
|
|
984
1012
|
awaitSettlement: (receipt) => Effect.gen(function* () {
|
|
985
1013
|
const encoded = yield* encodeReceipt(receipt).pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`receipt encode failed: ${error.message}`) })));
|
|
986
1014
|
const response = yield* call(receipt.conversationId, "awaitSettlement", (stub) => stub.awaitSettlementEncoded(encoded));
|
|
987
|
-
return (yield* expect(receipt.conversationId, "awaitSettlement",
|
|
1015
|
+
return (yield* expect(receipt.conversationId, "awaitSettlement", SettlementReached, ClientAwaitHostFailure)(response)).settlement;
|
|
988
1016
|
}),
|
|
989
1017
|
awaitProgress: (conversationId, afterSequence) => Effect.gen(function* () {
|
|
990
1018
|
const waiterId = yield* crypto.randomUUIDv4.pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`awaitProgress cancellation identity generation failed: ${error.message}`) })));
|
|
@@ -993,7 +1021,7 @@ var CloudflareConversationClient = class CloudflareConversationClient extends Co
|
|
|
993
1021
|
waiterId
|
|
994
1022
|
});
|
|
995
1023
|
const encoded = yield* encodeAwaitProgressRequest(request).pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`awaitProgress request encode failed: ${error.message}`) })));
|
|
996
|
-
const attempt = (retry) => call(conversationId, "awaitProgress", (stub) => stub.awaitProgressEncoded(encoded)).pipe(Effect.flatMap(expect(conversationId, "awaitProgress",
|
|
1024
|
+
const attempt = (retry) => call(conversationId, "awaitProgress", (stub) => stub.awaitProgressEncoded(encoded)).pipe(Effect.flatMap(expect(conversationId, "awaitProgress", ProgressObserved, ClientObserveHostFailure)), Effect.asVoid, Effect.catchTag("ConversationClientError", (error) => error.retryable === true && error.overloaded !== true && retry < 5 ? Effect.sleep(Duration.millis(10 * 2 ** retry)).pipe(Effect.andThen(attempt(retry + 1))) : Effect.fail(error)));
|
|
997
1025
|
yield* attempt(0).pipe(Effect.onInterrupt(() => cancelProgress(conversationId, waiterId)));
|
|
998
1026
|
}),
|
|
999
1027
|
readPage,
|
|
@@ -1014,33 +1042,40 @@ var CloudflareConversationClient = class CloudflareConversationClient extends Co
|
|
|
1014
1042
|
abort: (conversationId, command) => Effect.gen(function* () {
|
|
1015
1043
|
const encoded = yield* encodeAbortCommand(command).pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`abort command encode failed: ${error.message}`) })));
|
|
1016
1044
|
const response = yield* call(conversationId, "abort", (stub) => stub.abortEncoded(encoded));
|
|
1017
|
-
return (yield* expect(conversationId, "abort",
|
|
1045
|
+
return (yield* expect(conversationId, "abort", AbortRecorded, ClientAbortHostFailure)(response)).intent;
|
|
1018
1046
|
}),
|
|
1019
1047
|
resolveApproval: (conversationId, command) => Effect.gen(function* () {
|
|
1020
1048
|
const encoded = yield* encodeApprovalDecisionCommand(command).pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`approval command encode failed: ${error.message}`) })));
|
|
1021
1049
|
const response = yield* call(conversationId, "resolveApproval", (stub) => stub.resolveApprovalEncoded(encoded));
|
|
1022
|
-
return (yield* expect(conversationId, "resolveApproval",
|
|
1050
|
+
return (yield* expect(conversationId, "resolveApproval", ApprovalRecorded, ClientApprovalHostFailure)(response)).intent;
|
|
1023
1051
|
}),
|
|
1024
1052
|
resolveUnknown: (conversationId, command) => Effect.gen(function* () {
|
|
1025
1053
|
const encoded = yield* encodeUnknownResolutionCommand(command).pipe(Effect.mapError((error) => HostProtocolError.make({ message: boundHostDiagnostic(`resolution command encode failed: ${error.message}`) })));
|
|
1026
1054
|
const response = yield* call(conversationId, "resolveUnknown", (stub) => stub.resolveUnknownEncoded(encoded));
|
|
1027
|
-
return (yield* expect(conversationId, "resolveUnknown",
|
|
1055
|
+
return (yield* expect(conversationId, "resolveUnknown", UnknownResolutionRecorded, ClientUnknownHostFailure)(response)).intent;
|
|
1028
1056
|
})
|
|
1029
1057
|
});
|
|
1030
1058
|
}));
|
|
1031
1059
|
};
|
|
1032
1060
|
//#endregion
|
|
1033
1061
|
//#region src/conversation-object.ts
|
|
1034
|
-
/**
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1062
|
+
/** Classify only a decoded port request so new protocol members cannot bypass pre-arming. */
|
|
1063
|
+
const isMutatingPortRequest = (request) => {
|
|
1064
|
+
switch (request._tag) {
|
|
1065
|
+
case "LedgerAdmit":
|
|
1066
|
+
case "LedgerMarkReady":
|
|
1067
|
+
case "LedgerRequestAbort":
|
|
1068
|
+
case "LedgerRecordChildSettled":
|
|
1069
|
+
case "StoreMaterialize":
|
|
1070
|
+
case "StoreAppend": return true;
|
|
1071
|
+
case "LedgerLookup":
|
|
1072
|
+
case "LedgerResolveAdmission":
|
|
1073
|
+
case "StoreReadPage":
|
|
1074
|
+
case "StoreInspectTail":
|
|
1075
|
+
case "StoreExport": return false;
|
|
1076
|
+
}
|
|
1077
|
+
return false;
|
|
1078
|
+
};
|
|
1044
1079
|
/** The literal encoded `PortFailed(PortProtocolError)` fallback (same shape as WP2's). */
|
|
1045
1080
|
const encodedPortProtocolFailure = (message) => ({
|
|
1046
1081
|
_tag: "PortFailed",
|
|
@@ -1253,10 +1288,18 @@ const portCallEndpoint = (encoded) => Effect.gen(function* () {
|
|
|
1253
1288
|
const ports = yield* ConversationObjectPorts;
|
|
1254
1289
|
const maintenance = yield* ConversationMaintenance;
|
|
1255
1290
|
const alarm = yield* DurableAlarmService;
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1291
|
+
const decoded = yield* decodePortRequest(encoded).pipe(Effect.map((request) => ({
|
|
1292
|
+
_tag: "success",
|
|
1293
|
+
request
|
|
1294
|
+
})), Effect.catch((error) => Effect.succeed({
|
|
1295
|
+
_tag: "failure",
|
|
1296
|
+
message: error.message
|
|
1297
|
+
})));
|
|
1298
|
+
if (decoded._tag === "failure") return encodedPortProtocolFailure(`The port request could not be decoded: ${decoded.message}`);
|
|
1299
|
+
const mutating = isMutatingPortRequest(decoded.request);
|
|
1300
|
+
const handled = yield* (mutating ? maintenance.withMutation(ports.handle(decoded.request)) : ports.handle(decoded.request)).pipe(Effect.exit);
|
|
1258
1301
|
if (handled._tag === "Failure") return encodedPortProtocolFailure("The owner Object could not arm its maintenance alarm before the mutation.");
|
|
1259
|
-
const response = handled.value;
|
|
1302
|
+
const response = yield* encodePortResponse(handled.value).pipe(Effect.catch((error) => Effect.succeed(encodedPortProtocolFailure(`The port response could not be encoded: ${error.message}`))));
|
|
1260
1303
|
if (mutating) yield* alarm.scheduleNow.pipe(Effect.catch((error) => Effect.logWarning("ConversationObject.portCall: immediate re-arm failed", error)));
|
|
1261
1304
|
return response;
|
|
1262
1305
|
});
|
|
@@ -1343,36 +1386,32 @@ const makeConversationObjectClass = (options, observability) => {
|
|
|
1343
1386
|
* The Cloudflare Dynamic Worker `CodeExecutor` adapter (C4 of ADR-0017;
|
|
1344
1387
|
* DEPLOY-011). Each pass loads one fresh Worker through the Worker Loader
|
|
1345
1388
|
* with `globalOutbound: null`, so generated code has no ambient network,
|
|
1346
|
-
* bindings, or secrets; its only authority is the pass-scoped
|
|
1347
|
-
* routes back
|
|
1348
|
-
*
|
|
1349
|
-
*
|
|
1350
|
-
*
|
|
1351
|
-
*
|
|
1352
|
-
* isolate.
|
|
1389
|
+
* bindings, or secrets; its only authority is the pass-scoped RPC target that
|
|
1390
|
+
* routes back into the owning event's `CodeExecutionHost` service. Platform
|
|
1391
|
+
* CPU limits stop synchronous runaway programs; the executor-owned wall-clock
|
|
1392
|
+
* deadline interrupts asynchronously suspended passes. Deployment class `E`
|
|
1393
|
+
* only: the adapter records no persistent state and a later pass may run in a
|
|
1394
|
+
* completely different isolate.
|
|
1353
1395
|
*/
|
|
1354
1396
|
const dynamicWorkerImplementation = SandboxImplementation.make({
|
|
1355
1397
|
isolation: "isolated",
|
|
1356
1398
|
identity: "cloudflare-dynamic-worker"
|
|
1357
1399
|
});
|
|
1358
1400
|
/**
|
|
1359
|
-
*
|
|
1360
|
-
*
|
|
1361
|
-
* `
|
|
1401
|
+
* One object-capability endpoint for one execution pass. Workers RPC invokes
|
|
1402
|
+
* the target in the request context where it was created, so the native
|
|
1403
|
+
* Promise returned by `dispatch` and the Effect fiber that settles it share
|
|
1404
|
+
* one I/O owner. Passing the target as `run()`'s argument also scopes the
|
|
1405
|
+
* remote stub to that RPC call; no request state lives at module scope.
|
|
1362
1406
|
*/
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
var CodeModeHostEntrypoint = class extends WorkerEntrypoint {
|
|
1372
|
-
async call(passId, hostCall) {
|
|
1373
|
-
const pass = passRegistry.get(String(passId));
|
|
1374
|
-
if (pass === void 0) throw new Error("Unknown Code Mode pass");
|
|
1375
|
-
return pass.dispatch(hostCall);
|
|
1407
|
+
var CodeModePassHostTarget = class extends RpcTarget {
|
|
1408
|
+
#dispatch;
|
|
1409
|
+
constructor(dispatch) {
|
|
1410
|
+
super();
|
|
1411
|
+
this.#dispatch = dispatch;
|
|
1412
|
+
}
|
|
1413
|
+
call(hostCall) {
|
|
1414
|
+
return this.#dispatch(hostCall);
|
|
1376
1415
|
}
|
|
1377
1416
|
};
|
|
1378
1417
|
/**
|
|
@@ -1407,9 +1446,8 @@ const safeJson = (value) => {
|
|
|
1407
1446
|
};
|
|
1408
1447
|
|
|
1409
1448
|
export default class CodeModeHarness extends WorkerEntrypoint {
|
|
1410
|
-
async run() {
|
|
1411
|
-
const config =
|
|
1412
|
-
const host = this.env.CODE_MODE_HOST;
|
|
1449
|
+
async run(host) {
|
|
1450
|
+
const config = this.env.CODE_MODE_PASS;
|
|
1413
1451
|
const limits = config.limits;
|
|
1414
1452
|
const logs = [];
|
|
1415
1453
|
let logBytes = 0;
|
|
@@ -1447,7 +1485,7 @@ export default class CodeModeHarness extends WorkerEntrypoint {
|
|
|
1447
1485
|
};
|
|
1448
1486
|
throw new Error("code-mode host-call argument limit exceeded");
|
|
1449
1487
|
}
|
|
1450
|
-
const outcome = await host.call(
|
|
1488
|
+
const outcome = await host.call({
|
|
1451
1489
|
namespace,
|
|
1452
1490
|
method,
|
|
1453
1491
|
argument: JSON.parse(argText),
|
|
@@ -1527,24 +1565,16 @@ export default class CodeModeHarness extends WorkerEntrypoint {
|
|
|
1527
1565
|
}
|
|
1528
1566
|
`;
|
|
1529
1567
|
const BoundedLogs = Schema.Array(Schema.String.check(Schema.isMaxLength(16 * 1024))).check(Schema.isMaxLength(4096));
|
|
1530
|
-
const HarnessCompleted = Schema.
|
|
1531
|
-
_tag: Schema.Literal("completed"),
|
|
1568
|
+
const HarnessCompleted = Schema.TaggedStruct("completed", {
|
|
1532
1569
|
value: Schema.Json,
|
|
1533
1570
|
logs: BoundedLogs,
|
|
1534
1571
|
hostCalls: Schema.Natural,
|
|
1535
1572
|
logBytes: Schema.Natural,
|
|
1536
1573
|
resultBytes: Schema.Natural
|
|
1537
1574
|
});
|
|
1538
|
-
const HarnessSourceInvalid = Schema.
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
});
|
|
1542
|
-
const HarnessNotAFunction = Schema.Struct({
|
|
1543
|
-
_tag: Schema.Literal("source-not-a-function"),
|
|
1544
|
-
actual: Schema.String
|
|
1545
|
-
});
|
|
1546
|
-
const HarnessProgramFailed = Schema.Struct({
|
|
1547
|
-
_tag: Schema.Literal("program-failed"),
|
|
1575
|
+
const HarnessSourceInvalid = Schema.TaggedStruct("source-invalid", { message: Schema.String });
|
|
1576
|
+
const HarnessNotAFunction = Schema.TaggedStruct("source-not-a-function", { actual: Schema.String });
|
|
1577
|
+
const HarnessProgramFailed = Schema.TaggedStruct("program-failed", {
|
|
1548
1578
|
reason: Schema.Literals([
|
|
1549
1579
|
"threw",
|
|
1550
1580
|
"rejected",
|
|
@@ -1554,29 +1584,20 @@ const HarnessProgramFailed = Schema.Struct({
|
|
|
1554
1584
|
message: Schema.String,
|
|
1555
1585
|
logs: BoundedLogs
|
|
1556
1586
|
});
|
|
1557
|
-
const HarnessLogLimit = Schema.
|
|
1558
|
-
_tag: Schema.Literal("log-limit"),
|
|
1587
|
+
const HarnessLogLimit = Schema.TaggedStruct("log-limit", {
|
|
1559
1588
|
observed: Schema.Natural,
|
|
1560
1589
|
logs: BoundedLogs
|
|
1561
1590
|
});
|
|
1562
|
-
const HarnessArgumentLimit = Schema.
|
|
1563
|
-
_tag: Schema.Literal("argument-limit"),
|
|
1591
|
+
const HarnessArgumentLimit = Schema.TaggedStruct("argument-limit", {
|
|
1564
1592
|
observed: Schema.Natural,
|
|
1565
1593
|
logs: BoundedLogs
|
|
1566
1594
|
});
|
|
1567
|
-
const HarnessResultLimit = Schema.
|
|
1568
|
-
_tag: Schema.Literal("result-limit"),
|
|
1595
|
+
const HarnessResultLimit = Schema.TaggedStruct("result-limit", {
|
|
1569
1596
|
observed: Schema.Natural,
|
|
1570
1597
|
logs: BoundedLogs
|
|
1571
1598
|
});
|
|
1572
|
-
const HarnessHostCallLimit = Schema.
|
|
1573
|
-
|
|
1574
|
-
logs: BoundedLogs
|
|
1575
|
-
});
|
|
1576
|
-
const HarnessProtocol = Schema.Struct({
|
|
1577
|
-
_tag: Schema.Literal("protocol"),
|
|
1578
|
-
message: Schema.String
|
|
1579
|
-
});
|
|
1599
|
+
const HarnessHostCallLimit = Schema.TaggedStruct("host-call-limit", { logs: BoundedLogs });
|
|
1600
|
+
const HarnessProtocol = Schema.TaggedStruct("protocol", { message: Schema.String });
|
|
1580
1601
|
const HarnessOutcome = Schema.Union([
|
|
1581
1602
|
HarnessCompleted,
|
|
1582
1603
|
HarnessSourceInvalid,
|
|
@@ -1588,6 +1609,21 @@ const HarnessOutcome = Schema.Union([
|
|
|
1588
1609
|
HarnessHostCallLimit,
|
|
1589
1610
|
HarnessProtocol
|
|
1590
1611
|
]);
|
|
1612
|
+
const HarnessPassConfig = Schema.Struct({
|
|
1613
|
+
namespaces: Schema.Array(Schema.Struct({
|
|
1614
|
+
name: Schema.NonEmptyString,
|
|
1615
|
+
methods: Schema.Array(Schema.NonEmptyString).check(Schema.isMaxLength(64))
|
|
1616
|
+
})).check(Schema.isMaxLength(32)),
|
|
1617
|
+
limits: Schema.Struct({
|
|
1618
|
+
maxLogBytes: Schema.Natural,
|
|
1619
|
+
maxResultBytes: Schema.Natural,
|
|
1620
|
+
maxHostCalls: Schema.Natural,
|
|
1621
|
+
maxHostCallArgumentBytes: Schema.Natural
|
|
1622
|
+
})
|
|
1623
|
+
});
|
|
1624
|
+
const encodeHarnessPassConfig = Schema.encodeSync(HarnessPassConfig);
|
|
1625
|
+
const encodeJsonPayload = Schema.encodeSync(Schema.fromJsonString(Schema.Json));
|
|
1626
|
+
const decodeJsonPayload = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json));
|
|
1591
1627
|
const decodeHarnessOutcome = (value) => {
|
|
1592
1628
|
try {
|
|
1593
1629
|
return Schema.decodeUnknownOption(HarnessOutcome)(value);
|
|
@@ -1609,11 +1645,20 @@ const decodeHostCallResult = (value) => {
|
|
|
1609
1645
|
return Option.none();
|
|
1610
1646
|
}
|
|
1611
1647
|
};
|
|
1648
|
+
/** Dispose a Cloudflare RPC handle when the runtime supplies its untyped disposal hook. @internal */
|
|
1649
|
+
const disposeRpcHandle = (handle) => Effect.try({
|
|
1650
|
+
try: () => {
|
|
1651
|
+
if (typeof handle !== "object" && typeof handle !== "function" || handle === null) return;
|
|
1652
|
+
if (!(Symbol.dispose in handle)) return;
|
|
1653
|
+
const dispose = Reflect.get(handle, Symbol.dispose);
|
|
1654
|
+
if (typeof dispose === "function") Reflect.apply(dispose, handle, []);
|
|
1655
|
+
},
|
|
1656
|
+
catch: (cause) => safeCauseDiagnostic(cause, "The Cloudflare RPC disposal hook failed without a diagnostic")
|
|
1657
|
+
}).pipe(Effect.catch((diagnostic) => Effect.logWarning(`Cloudflare RPC handle disposal failed: ${diagnostic}`).pipe(Effect.ignoreCause)));
|
|
1612
1658
|
const encodeHostResultPayload = (outcome) => {
|
|
1613
1659
|
try {
|
|
1614
1660
|
const payload = outcome._tag === "CodeHostCallSuccess" ? outcome.value : outcome.error;
|
|
1615
|
-
const encodedPayload =
|
|
1616
|
-
if (encodedPayload === void 0) return void 0;
|
|
1661
|
+
const encodedPayload = encodeJsonPayload(payload);
|
|
1617
1662
|
return {
|
|
1618
1663
|
encodedPayload,
|
|
1619
1664
|
resultBytes: utf8ByteLength(encodedPayload)
|
|
@@ -1632,8 +1677,7 @@ const utf8ByteLength = (value) => {
|
|
|
1632
1677
|
};
|
|
1633
1678
|
/** Reserved global names the harness owns inside the dynamic worker. */
|
|
1634
1679
|
const reservedHarnessGlobals = /* @__PURE__ */ new Set(["console"]);
|
|
1635
|
-
const
|
|
1636
|
-
const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(function* (request) {
|
|
1680
|
+
const makeExecute = (options, clock) => Effect.fn("DynamicWorkerCodeExecutor.execute")(function* (request) {
|
|
1637
1681
|
if (request.network._tag !== "NetworkDisabled") return yield* CodeExecutorUnsupportedError.make({
|
|
1638
1682
|
implementation: dynamicWorkerImplementation,
|
|
1639
1683
|
feature: "network",
|
|
@@ -1651,11 +1695,12 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1651
1695
|
message: `Namespace ${namespace.name} collides with a harness binding`
|
|
1652
1696
|
});
|
|
1653
1697
|
const host = yield* CodeExecutionHost;
|
|
1654
|
-
|
|
1655
|
-
const
|
|
1656
|
-
const
|
|
1657
|
-
|
|
1658
|
-
|
|
1698
|
+
const startedAt = clock.monotonicTimeNanosUnsafe();
|
|
1699
|
+
const passDeadline = startedAt + Duration.toNanosUnsafe(request.limits.maxWallTime);
|
|
1700
|
+
const remainingPassWallTime = () => {
|
|
1701
|
+
const now = clock.monotonicTimeNanosUnsafe();
|
|
1702
|
+
return Duration.nanos(passDeadline > now ? passDeadline - now : 0n);
|
|
1703
|
+
};
|
|
1659
1704
|
let issuedHostCalls = 0;
|
|
1660
1705
|
let passOpen = true;
|
|
1661
1706
|
const queuedHostCalls = [];
|
|
@@ -1689,13 +1734,21 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1689
1734
|
failPass(error);
|
|
1690
1735
|
return yield* error;
|
|
1691
1736
|
}
|
|
1692
|
-
const normalizedPayload =
|
|
1737
|
+
const normalizedPayload = decodeJsonPayload(encoded.encodedPayload);
|
|
1738
|
+
if (Option.isNone(normalizedPayload)) {
|
|
1739
|
+
const error = CodeExecutionProtocolError.make({
|
|
1740
|
+
implementation: dynamicWorkerImplementation,
|
|
1741
|
+
message: "The execution host returned a result that could not cross the JSON boundary"
|
|
1742
|
+
});
|
|
1743
|
+
failPass(error);
|
|
1744
|
+
return yield* error;
|
|
1745
|
+
}
|
|
1693
1746
|
queued.resolve(decoded.value._tag === "CodeHostCallSuccess" ? {
|
|
1694
1747
|
_tag: "CodeHostCallSuccess",
|
|
1695
|
-
value: normalizedPayload
|
|
1748
|
+
value: normalizedPayload.value
|
|
1696
1749
|
} : {
|
|
1697
1750
|
_tag: "CodeHostCallFailure",
|
|
1698
|
-
error: normalizedPayload
|
|
1751
|
+
error: normalizedPayload.value
|
|
1699
1752
|
});
|
|
1700
1753
|
});
|
|
1701
1754
|
const server = yield* Effect.gen(function* () {
|
|
@@ -1756,12 +1809,9 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1756
1809
|
};
|
|
1757
1810
|
const closeAdmission = Effect.sync(() => {
|
|
1758
1811
|
passOpen = false;
|
|
1759
|
-
passRegistry.delete(passId);
|
|
1760
1812
|
rejectQueuedHostCalls(/* @__PURE__ */ new Error("Code Mode pass is closing"));
|
|
1761
1813
|
});
|
|
1762
|
-
yield* Effect.
|
|
1763
|
-
passRegistry.set(passId, { dispatch });
|
|
1764
|
-
}), () => closeAdmission.pipe(Effect.andThen(Fiber.interrupt(server))));
|
|
1814
|
+
yield* Effect.addFinalizer(() => closeAdmission.pipe(Effect.andThen(Fiber.interrupt(server))));
|
|
1765
1815
|
const workerCode = {
|
|
1766
1816
|
compatibilityDate: options.compatibilityDate ?? "2025-05-01",
|
|
1767
1817
|
mainModule: "harness.js",
|
|
@@ -1769,22 +1819,18 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1769
1819
|
"harness.js": HARNESS_MODULE,
|
|
1770
1820
|
"program.js": `export default (\n${request.source}\n);`
|
|
1771
1821
|
},
|
|
1772
|
-
env: {
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
maxHostCallArgumentBytes: request.limits.maxHostCallArgumentBytes
|
|
1785
|
-
}
|
|
1786
|
-
})
|
|
1787
|
-
},
|
|
1822
|
+
env: { CODE_MODE_PASS: encodeHarnessPassConfig({
|
|
1823
|
+
namespaces: request.namespaces.map((namespace) => ({
|
|
1824
|
+
name: namespace.name,
|
|
1825
|
+
methods: namespace.methods
|
|
1826
|
+
})),
|
|
1827
|
+
limits: {
|
|
1828
|
+
maxLogBytes: request.limits.maxLogBytes,
|
|
1829
|
+
maxResultBytes: request.limits.maxResultBytes,
|
|
1830
|
+
maxHostCalls: request.limits.maxHostCalls,
|
|
1831
|
+
maxHostCallArgumentBytes: request.limits.maxHostCallArgumentBytes
|
|
1832
|
+
}
|
|
1833
|
+
}) },
|
|
1788
1834
|
globalOutbound: null,
|
|
1789
1835
|
...request.limits.cpuMillis === void 0 ? {} : { limits: {
|
|
1790
1836
|
cpuMs: request.limits.cpuMillis,
|
|
@@ -1794,7 +1840,7 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1794
1840
|
const worker = yield* Effect.acquireRelease(Effect.try({
|
|
1795
1841
|
try: () => options.loader.load(workerCode),
|
|
1796
1842
|
catch: (cause) => {
|
|
1797
|
-
const text = cause
|
|
1843
|
+
const text = safeCauseMessage(cause, "The Worker Loader failed without a diagnostic");
|
|
1798
1844
|
if (/syntaxerror|failed to (compile|parse)/i.test(text)) return CodeSourceError.make({
|
|
1799
1845
|
implementation: dynamicWorkerImplementation,
|
|
1800
1846
|
reason: "invalid",
|
|
@@ -1806,13 +1852,13 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1806
1852
|
cause
|
|
1807
1853
|
});
|
|
1808
1854
|
}
|
|
1809
|
-
}),
|
|
1810
|
-
|
|
1811
|
-
|
|
1855
|
+
}), disposeRpcHandle);
|
|
1856
|
+
const entrypoint = yield* Effect.acquireRelease(Effect.try({
|
|
1857
|
+
try: () => worker.getEntrypoint(),
|
|
1858
|
+
catch: (cause) => classifyWorkerFailure(cause, request.limits.maxWallTime)
|
|
1859
|
+
}), disposeRpcHandle);
|
|
1812
1860
|
const rpc = Effect.tryPromise({
|
|
1813
|
-
try:
|
|
1814
|
-
return await worker.getEntrypoint().run();
|
|
1815
|
-
},
|
|
1861
|
+
try: () => entrypoint.run(new CodeModePassHostTarget(dispatch)),
|
|
1816
1862
|
catch: (cause) => classifyWorkerFailure(cause, request.limits.maxWallTime)
|
|
1817
1863
|
});
|
|
1818
1864
|
const exit = yield* Effect.raceFirst(rpc.pipe(Effect.timeoutOrElse({
|
|
@@ -1829,7 +1875,7 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1829
1875
|
if (passFailure !== void 0) return yield* passFailure;
|
|
1830
1876
|
if (Exit.isFailure(exit)) return yield* Effect.failCause(exit.cause);
|
|
1831
1877
|
const raw = exit.value;
|
|
1832
|
-
const finishedAt =
|
|
1878
|
+
const finishedAt = clock.monotonicTimeNanosUnsafe();
|
|
1833
1879
|
const outcome = decodeHarnessOutcome(raw);
|
|
1834
1880
|
if (Option.isNone(outcome)) return yield* CodeExecutionProtocolError.make({
|
|
1835
1881
|
implementation: dynamicWorkerImplementation,
|
|
@@ -1841,7 +1887,7 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1841
1887
|
value: outcome.value.value,
|
|
1842
1888
|
logs: outcome.value.logs,
|
|
1843
1889
|
resourceUse: CodeExecutionResourceUse.make({
|
|
1844
|
-
wallTime: Duration.
|
|
1890
|
+
wallTime: Duration.nanos(finishedAt > startedAt ? finishedAt - startedAt : 0n),
|
|
1845
1891
|
hostCalls: outcome.value.hostCalls,
|
|
1846
1892
|
logBytes: outcome.value.logBytes,
|
|
1847
1893
|
resultBytes: outcome.value.resultBytes
|
|
@@ -1902,13 +1948,7 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1902
1948
|
* than a fabricated program result.
|
|
1903
1949
|
*/
|
|
1904
1950
|
const classifyWorkerFailure = (cause, maxWallTime) => {
|
|
1905
|
-
const text = (
|
|
1906
|
-
try {
|
|
1907
|
-
return cause instanceof Error ? `${cause.name}: ${cause.message}` : String(cause);
|
|
1908
|
-
} catch {
|
|
1909
|
-
return "[unserializable worker failure]";
|
|
1910
|
-
}
|
|
1911
|
-
})();
|
|
1951
|
+
const text = safeCauseDiagnostic(cause, "[unserializable worker failure]");
|
|
1912
1952
|
if (/syntaxerror|failed to (compile|parse)/i.test(text)) return CodeSourceError.make({
|
|
1913
1953
|
implementation: dynamicWorkerImplementation,
|
|
1914
1954
|
reason: "invalid",
|
|
@@ -1931,8 +1971,11 @@ const classifyWorkerFailure = (cause, maxWallTime) => {
|
|
|
1931
1971
|
});
|
|
1932
1972
|
};
|
|
1933
1973
|
/** Layer building the Dynamic Worker `CodeExecutor` from resolved bindings. */
|
|
1934
|
-
const dynamicWorkerCodeExecutorLayer = (options) => Layer.
|
|
1974
|
+
const dynamicWorkerCodeExecutorLayer = (options) => Layer.effect(CodeExecutor, Effect.gen(function* () {
|
|
1975
|
+
const clock = yield* Clock.Clock;
|
|
1976
|
+
return CodeExecutor.of({ execute: makeExecute(options, clock) });
|
|
1977
|
+
}));
|
|
1935
1978
|
//#endregion
|
|
1936
|
-
export { AbortRecorded, AdminExplainRequest, AdminFailed, AdminFailure, AdminResponse, AdminVerifyRequest, AdmissionLimitExceeded, ApprovalRecorded, AwaitProgressRequest, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CancelProgressRequest, CloudflareAdmissionLimitsValue, CloudflareBindingError, CloudflareConversationClient, CloudflareDurableRuntime, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError,
|
|
1979
|
+
export { AbortRecorded, AdminExplainRequest, AdminFailed, AdminFailure, AdminResponse, AdminVerifyRequest, AdmissionLimitExceeded, ApprovalRecorded, AwaitProgressRequest, CLOUDFLARE_DATABASE_CAP_BYTES, CLOUDFLARE_RUNTIME_DEFAULTS, CancelProgressRequest, CloudflareAdmissionLimitsValue, CloudflareBindingError, CloudflareConversationClient, CloudflareDurableRuntime, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError, ConversationClientError, ConversationMaintenance, ConversationMaintenanceFailpoint, ConversationObjectIdentity, ConversationObjectNamespace, ConversationObjectPorts, DEFAULT_MAX_DATABASE_BYTES, DurableAlarmError, DurableAlarmService, DurableObjectContext, ExplainedRecovery, HostFailed, HostFailure, HostProtocolError, HostResponse, MaintenancePassReport, ObligationsScanned, ObservePageRequest, ObservedPage, ProgressCancelled, ProgressObserved, ProgressWaitRegistry, RetryExecuted, SettlementReached, SubmitRequest, SubmitSucceeded, UnknownResolutionRecorded, VerifiedIntegrity, boundHostDiagnostic, cloudflareWakeSchedulerLayer, conversationNamespaceFromEnv, conversationNamespaceLayer, conversationPortTransportLayer, decodeAbortCommand, decodeAdminExplainRequest, decodeAdminResponse, decodeAdminVerifyRequest, decodeApprovalDecisionCommand, decodeAwaitProgressRequest, decodeCancelProgressRequest, decodeHostResponse, decodeObligationThresholds, decodeObservePageRequest, decodeReceipt, decodeRetryCommand, decodeSubmitRequest, decodeUnknownResolutionCommand, disposeRpcHandle, dynamicWorkerCodeExecutorLayer, dynamicWorkerImplementation, encodeAbortCommand, encodeAdminResponse, encodeApprovalDecisionCommand, encodeAwaitProgressRequest, encodeCancelProgressRequest, encodeHostResponse, encodeObservePageRequest, encodeReceipt, encodeSubmitRequest, encodeUnknownResolutionCommand, makeConversationObjectClass };
|
|
1937
1980
|
|
|
1938
1981
|
//# sourceMappingURL=index.mjs.map
|