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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/Alarm.d.mts +188 -0
  2. package/dist/Alarm.mjs +451 -0
  3. package/dist/Alarm.mjs.map +1 -0
  4. package/dist/BrowserRestCapture.d.mts +35 -0
  5. package/dist/BrowserRestCapture.mjs +238 -0
  6. package/dist/BrowserRestCapture.mjs.map +1 -0
  7. package/dist/BrowserRestCrawl.d.mts +17 -0
  8. package/dist/BrowserRestCrawl.mjs +244 -0
  9. package/dist/BrowserRestCrawl.mjs.map +1 -0
  10. package/dist/CloudflareAiGateway.d.mts +64 -0
  11. package/dist/CloudflareAiGateway.mjs +70 -0
  12. package/dist/CloudflareAiGateway.mjs.map +1 -0
  13. package/dist/CloudflareBindings.d.mts +115 -0
  14. package/dist/CloudflareBindings.mjs +113 -0
  15. package/dist/CloudflareBindings.mjs.map +1 -0
  16. package/dist/CloudflareBrowser-DV6kya1H.mjs +495 -0
  17. package/dist/CloudflareBrowser-DV6kya1H.mjs.map +1 -0
  18. package/dist/CloudflareBrowser-Doe1M7R5.d.mts +80 -0
  19. package/dist/CloudflareBrowser.d.mts +2 -0
  20. package/dist/CloudflareBrowser.mjs +2 -0
  21. package/dist/CloudflareCodeMode.d.mts +44 -0
  22. package/dist/CloudflareCodeMode.mjs +616 -0
  23. package/dist/CloudflareCodeMode.mjs.map +1 -0
  24. package/dist/CloudflareConfig-f3CqTel1.d.mts +101 -0
  25. package/dist/CloudflareConfig.d.mts +2 -0
  26. package/dist/CloudflareConfig.mjs +122 -0
  27. package/dist/CloudflareConfig.mjs.map +1 -0
  28. package/dist/CloudflareMemory.d.mts +142 -0
  29. package/dist/CloudflareMemory.mjs +202 -0
  30. package/dist/CloudflareMemory.mjs.map +1 -0
  31. package/dist/CloudflareScheduling.d.mts +52 -0
  32. package/dist/CloudflareScheduling.mjs +396 -0
  33. package/dist/CloudflareScheduling.mjs.map +1 -0
  34. package/dist/CloudflareSubscriptions.d.mts +87 -0
  35. package/dist/CloudflareSubscriptions.mjs +530 -0
  36. package/dist/CloudflareSubscriptions.mjs.map +1 -0
  37. package/dist/CloudflareThreadClient.d.mts +1496 -0
  38. package/dist/CloudflareThreadClient.mjs +382 -0
  39. package/dist/CloudflareThreadClient.mjs.map +1 -0
  40. package/dist/InteractiveBrowser-DUE_m12-.d.mts +161 -0
  41. package/dist/InteractiveBrowser-DupDtfw2.mjs +1169 -0
  42. package/dist/InteractiveBrowser-DupDtfw2.mjs.map +1 -0
  43. package/dist/InteractiveBrowser.d.mts +2 -0
  44. package/dist/InteractiveBrowser.mjs +2 -0
  45. package/dist/ProtectedBrowser.d.mts +123 -0
  46. package/dist/ProtectedBrowser.mjs +1135 -0
  47. package/dist/ProtectedBrowser.mjs.map +1 -0
  48. package/dist/ThreadObject-CIbmXZCY.d.mts +812 -0
  49. package/dist/ThreadObject-DvRG0dDz.mjs +835 -0
  50. package/dist/ThreadObject-DvRG0dDz.mjs.map +1 -0
  51. package/dist/ThreadObject.d.mts +2 -0
  52. package/dist/ThreadObject.mjs +3 -0
  53. package/dist/WakeScheduler.d.mts +23 -0
  54. package/dist/WakeScheduler.mjs +57 -0
  55. package/dist/WakeScheduler.mjs.map +1 -0
  56. package/dist/boundary-BguazVkh.mjs +42 -0
  57. package/dist/boundary-BguazVkh.mjs.map +1 -0
  58. package/dist/index.d.mts +13 -1548
  59. package/dist/index.mjs +13 -1636
  60. package/dist/prepared-admission-DU0_T-ev.mjs +73 -0
  61. package/dist/prepared-admission-DU0_T-ev.mjs.map +1 -0
  62. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  63. package/package.json +1 -53
  64. package/src/Alarm.ts +973 -0
  65. package/src/BrowserRestCapture.ts +477 -0
  66. package/src/BrowserRestCrawl.ts +540 -0
  67. package/src/CloudflareAiGateway.ts +170 -0
  68. package/src/CloudflareBindings.ts +199 -0
  69. package/src/CloudflareBrowser.ts +15 -0
  70. package/src/{code-mode-executor.ts → CloudflareCodeMode.ts} +372 -204
  71. package/src/{config.ts → CloudflareConfig.ts} +9 -8
  72. package/src/CloudflareMemory.ts +420 -0
  73. package/src/CloudflareScheduling.ts +747 -0
  74. package/src/CloudflareSubscriptions.ts +1041 -0
  75. package/src/CloudflareThreadClient.ts +809 -0
  76. package/src/InteractiveBrowser.ts +2354 -0
  77. package/src/ProtectedBrowser.ts +9 -0
  78. package/src/ThreadObject.ts +1179 -0
  79. package/src/{wake-scheduler.ts → WakeScheduler.ts} +32 -26
  80. package/src/index.ts +12 -23
  81. package/src/internal/boundary.ts +55 -0
  82. package/src/internal/browser-quick-action.ts +857 -0
  83. package/src/internal/browser-session-lifecycle.ts +160 -0
  84. package/src/internal/layers.ts +751 -0
  85. package/src/internal/message-delivery.ts +152 -0
  86. package/src/internal/prepared-admission.ts +116 -0
  87. package/src/internal/progress-wait.ts +121 -0
  88. package/src/internal/transport.ts +44 -0
  89. package/src/protected-browser/binding.ts +313 -0
  90. package/src/protected-browser/host.ts +344 -0
  91. package/src/protected-browser/inspect-frame.ts +125 -0
  92. package/src/protected-browser/native.ts +445 -0
  93. package/src/protected-browser/policy.ts +821 -0
  94. package/dist/index.mjs.map +0 -1
  95. package/src/alarm.ts +0 -334
  96. package/src/bindings.ts +0 -145
  97. package/src/client.ts +0 -646
  98. package/src/conversation-object.ts +0 -768
  99. package/src/layers.ts +0 -376
  100. package/src/transport.ts +0 -38
@@ -0,0 +1,835 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { DurableObjectContext, ThreadObjectIdentity, ThreadObjectNamespace, ThreadObjectPlacement, callThreadObject, threadNamespaceFromEnv } from "./CloudflareBindings.mjs";
3
+ import { AdmissionLimitExceeded, CLOUDFLARE_RUNTIME_DEFAULTS, CloudflareDurableRuntimeConfig, CloudflareDurableRuntimeConfigValue, CloudflarePlatformConfigError } from "./CloudflareConfig.mjs";
4
+ import { DurableAlarmError, DurableAlarmService, ThreadMaintenance, ThreadMaintenanceFailpoint, ThreadMessageDelivery, ThreadMutationGate, ThreadPublication, publishCommitted } from "./Alarm.mjs";
5
+ import { AbortRecorded, ApprovalRecorded, CloudflareThreadClient, HostFailed, HostProtocolError, ObservedPage, ProgressCancelled, ProgressObserved, SettlementReached, SubmissionStatusResponse, SubmitSucceeded, UnknownResolutionRecorded, boundHostDiagnostic, decodeAbortCommand, decodeApprovalDecisionCommand, decodeAwaitProgressRequest, decodeCancelProgressRequest, decodeObservePageRequest, decodeReceipt, decodeSubmitRequest, decodeUnknownResolutionCommand, encodeHostResponse } from "./CloudflareThreadClient.mjs";
6
+ import { t as cloudflarePreparedInputAdmissionLayer } from "./prepared-admission-DU0_T-ev.mjs";
7
+ import { cloudflareWakeSchedulerLayer } from "./WakeScheduler.mjs";
8
+ import { Cause, Clock, Context, Deferred, Duration, Effect, Layer, Option, Ref, Schema, Semaphore, Stream } from "effect";
9
+ import { compileRegistrations } from "effect-agent/agent-registration";
10
+ import { DurableAgentRuntime, DurableRuntimeConfig, RecoveryReport } from "effect-agent/durable-agent-runtime";
11
+ import { SubmissionId, ThreadId } from "effect-agent/identifiers";
12
+ import { AdmissionPolicyError, LedgerError, OwnershipLost, SettlementConflict, SubmissionLedger, SubmissionLookupById, SubmissionLookupByKey } from "effect-agent/submission-ledger";
13
+ import { ThreadProjectionMaintenance } from "effect-agent/thread-projection-maintenance";
14
+ import { SqlClient } from "effect/unstable/sql/SqlClient";
15
+ import { PersistedJson, ProducerId } from "effect-agent/records";
16
+ import { DurableObject, DurableObjectState, WorkerEnvironment } from "effect-cf";
17
+ import { BrowserCrypto } from "@effect/platform-browser";
18
+ import { SqliteClient } from "@effect/sql-sqlite-do";
19
+ import { DigestError } from "effect-agent/digest";
20
+ import { DurableRuntimeFailpoint, DurableRuntimeFailpointError } from "effect-agent/durable-failpoint";
21
+ import { OperationAuthorizationRequest, OperationAuthorizer, OperationDenied, operationAuthorizerLayer } from "effect-agent/operation-authorizer";
22
+ import { AppendConflict, FenceRejected, ThreadNotMaterialized, ThreadRead, ThreadStore, ThreadStoreError } from "effect-agent/thread-store";
23
+ import "effect-agent/prepared-input-admission";
24
+ import { LedgerLookupResult, PortFailed, PortProtocolError, PortSucceeded, decodePortRequest, encodePortResponse } from "@effect-agent/storage-cloudflare/port-protocol";
25
+ import { IntegrityReport, ObligationReport, ObligationThresholds, RecoveryExplanation, RetryCommand, RetryRefused } from "effect-agent/admin";
26
+ import { RunJournalError } from "effect-agent/run-journal";
27
+ import { WakeScheduler } from "effect-agent/wake-scheduler";
28
+ import { doMessageDeliveryStoreLayer } from "@effect-agent/storage-cloudflare/do-message-delivery-store";
29
+ import "@effect-agent/storage-cloudflare/do-storage-failpoint";
30
+ import { submissionLedgerLayer } from "@effect-agent/storage-cloudflare/do-submission-ledger";
31
+ import { storageConfigLayer, storageFailpointLayer, threadStoreLayer } from "@effect-agent/storage-cloudflare/do-thread-store";
32
+ import { ThreadPortTransport, executePortRequest, portTransportFailure, routedSubmissionLedgerLayer, routedThreadStoreLayer } from "@effect-agent/storage-cloudflare/port-routing";
33
+ import { MessageDeliveryDriver, MessageDeliveryError, MessageDeliveryStore } from "effect-agent/message-delivery";
34
+ import { CurrentToolFailureObserver, RunContextPreparationPassthrough, RunToolAuthorization, toolFailureObserverLayer } from "effect-agent/run-options";
35
+ import { ToolReconciler } from "effect-agent/tool-reconciler";
36
+ //#region src/internal/message-delivery.ts
37
+ /** Every write prearms its owner; the delivery due index owns its recovery deadline. */
38
+ const guardedMessageDeliveryStoreLayer = Layer.effect(MessageDeliveryStore, Effect.gen(function* () {
39
+ const store = yield* MessageDeliveryStore;
40
+ const mutations = yield* ThreadMutationGate;
41
+ const wakes = yield* WakeScheduler;
42
+ const { ownsThread } = yield* ThreadObjectPlacement;
43
+ const deadline = yield* Ref.make(void 0);
44
+ const cacheGate = yield* Semaphore.make(1);
45
+ const local = (owner, body) => owner === void 0 || ownsThread(owner) ? body : Effect.fail(MessageDeliveryError.make({
46
+ reason: "validation",
47
+ operation: "message owner"
48
+ }));
49
+ const mutate = (body) => mutations.withMutation(cacheGate.withPermit(Ref.set(deadline, void 0).pipe(Effect.andThen(body))), { invalidatesRecovery: false }).pipe(Effect.catchTag("DurableAlarmError", () => MessageDeliveryError.make({
50
+ reason: "storage",
51
+ operation: "prearm message delivery"
52
+ })));
53
+ const nextDeadline = cacheGate.withPermit(Effect.gen(function* () {
54
+ const cached = yield* Ref.get(deadline);
55
+ if (cached !== void 0) return cached;
56
+ const current = yield* store.nextDeadline();
57
+ yield* Ref.set(deadline, current);
58
+ return current;
59
+ }));
60
+ return MessageDeliveryStore.of({
61
+ limits: store.limits,
62
+ maxStoredValueBytes: store.maxStoredValueBytes,
63
+ insert: (record) => local(record.key.ownerThreadId, mutate(store.insert(record)).pipe(Effect.tap(() => wakes.notify(record.key.ownerThreadId)))),
64
+ get: (key) => local(key.ownerThreadId, store.get(key)),
65
+ list: (request) => local(request.ownerThreadId, store.list(request)),
66
+ change: (key, change) => local(key.ownerThreadId, mutate(store.change(key, change))),
67
+ due: (nowMillis, limit, owner) => local(owner, store.due(nowMillis, limit, owner)).pipe(Effect.filterOrFail((keys) => keys.every((key) => ownsThread(key.ownerThreadId)), () => MessageDeliveryError.make({
68
+ reason: "validation",
69
+ operation: "message owner"
70
+ }))),
71
+ nextDeadline: (owner) => local(owner, owner === void 0 ? nextDeadline : store.nextDeadline(owner))
72
+ });
73
+ }));
74
+ /** Message progress shares the native alarm slot without blocking source runtime work. */
75
+ const threadMessageDeliveryLayer = Layer.effectContext(Effect.gen(function* () {
76
+ const driver = yield* MessageDeliveryDriver;
77
+ const store = yield* MessageDeliveryStore;
78
+ const wakes = yield* WakeScheduler;
79
+ const config = yield* CloudflareDurableRuntimeConfig;
80
+ const failure = (operation) => () => DurableAlarmError.make({
81
+ operation,
82
+ message: "Durable message recovery remains pending"
83
+ });
84
+ const drain = Effect.gen(function* () {
85
+ const deadline = yield* store.nextDeadline();
86
+ if (deadline !== null && deadline <= (yield* Clock.currentTimeMillis)) yield* driver.runDue();
87
+ }).pipe(Effect.mapError(failure("drain message delivery")));
88
+ return Context.make(ThreadMessageDelivery, {
89
+ drain,
90
+ drainUntil: Effect.fn("ThreadMessageDelivery.drainUntil")(function* (finished) {
91
+ const notified = (yield* Stream.toPull(wakes.wakes)).pipe(Effect.catch(() => Effect.never));
92
+ yield* Effect.gen(function* () {
93
+ yield* drain;
94
+ const deadline = yield* store.nextDeadline().pipe(Effect.mapError(failure("read message deadline")));
95
+ const delay = deadline === null ? config.wakeScanInterval : Math.min(config.wakeScanInterval, Math.max(1, deadline - (yield* Clock.currentTimeMillis)));
96
+ yield* Effect.raceFirst(Deferred.await(finished), Effect.raceFirst(notified, Effect.sleep(delay)));
97
+ }).pipe(Effect.repeat({ until: () => Deferred.isDone(finished) }));
98
+ }, Effect.scoped),
99
+ pendingDeadline: store.nextDeadline().pipe(Effect.map(Option.fromNullishOr), Effect.mapError(failure("read message deadline")))
100
+ });
101
+ }));
102
+ //#endregion
103
+ //#region src/internal/progress-wait.ts
104
+ /** Cancellation tombstones are bounded hints, never durable authority. */
105
+ const MAX_CANCELLATION_TOMBSTONES = 1024;
106
+ /**
107
+ * Per-incarnation cancellation registry for long-lived progress RPCs. The public runtime owns
108
+ * the actual wake registration; this host-only registry lets an interrupted Worker Effect ask
109
+ * the Object to interrupt its scoped wait before the Worker execution context itself ends.
110
+ */
111
+ var ProgressWaitRegistry = class ProgressWaitRegistry extends Context.Service()("@effect-agent/platform-cloudflare/ProgressWaitRegistry") {
112
+ static layer = Layer.effect(ProgressWaitRegistry, Effect.gen(function* () {
113
+ const registrations = yield* Ref.make(/* @__PURE__ */ new Map());
114
+ const remove = (waiterId, deferred) => Ref.update(registrations, (current) => {
115
+ const existing = current.get(waiterId);
116
+ if (existing === void 0 || existing === "cancelled" || !existing.has(deferred)) return current;
117
+ const next = new Map(current);
118
+ const active = new Set(existing);
119
+ active.delete(deferred);
120
+ if (active.size === 0) next.delete(waiterId);
121
+ else next.set(waiterId, active);
122
+ return next;
123
+ });
124
+ const subscribe = Effect.fn("ProgressWaitRegistry.subscribe")((waiterId) => Effect.gen(function* () {
125
+ const deferred = yield* Deferred.make();
126
+ yield* Effect.addFinalizer(() => remove(waiterId, deferred));
127
+ return {
128
+ cancelled: yield* Ref.modify(registrations, (current) => {
129
+ const existing = current.get(waiterId);
130
+ const next = new Map(current);
131
+ if (existing === "cancelled") return [true, current];
132
+ const active = new Set(existing ?? []);
133
+ active.add(deferred);
134
+ next.set(waiterId, active);
135
+ return [false, next];
136
+ }),
137
+ deferred
138
+ };
139
+ }).pipe(Effect.map(({ cancelled, deferred }) => cancelled ? Effect.void : Deferred.await(deferred))));
140
+ const cancel = Effect.fn("ProgressWaitRegistry.cancel")(function* (waiterId) {
141
+ const waiters = yield* Ref.modify(registrations, (current) => {
142
+ const existing = current.get(waiterId);
143
+ if (existing === "cancelled") return [[], current];
144
+ const next = new Map(current);
145
+ next.delete(waiterId);
146
+ next.set(waiterId, "cancelled");
147
+ let tombstones = 0;
148
+ for (const registration of next.values()) if (registration === "cancelled") tombstones += 1;
149
+ if (tombstones > MAX_CANCELLATION_TOMBSTONES) for (const [id, registration] of next) {
150
+ if (registration !== "cancelled") continue;
151
+ next.delete(id);
152
+ break;
153
+ }
154
+ return [existing === void 0 ? [] : [...existing], next];
155
+ });
156
+ yield* Effect.forEach(waiters, (waiter) => Deferred.succeed(waiter, void 0), { discard: true });
157
+ }, Effect.uninterruptible);
158
+ return ProgressWaitRegistry.of({
159
+ subscribe,
160
+ cancel
161
+ });
162
+ }));
163
+ };
164
+ //#endregion
165
+ //#region src/internal/transport.ts
166
+ /**
167
+ * `ThreadPortTransport` over native Durable Object JS RPC (decision D-P6-3): one
168
+ * `portCall(envelope)` on the stub of the Object that owns the addressed Thread
169
+ * (`namespace.idFromName(threadId)` — the identity rule, plan §1.2). The envelopes are
170
+ * already Schema-encoded JSON, so the RPC boundary carries only structured-cloneable values;
171
+ * the protocol module stays transport-agnostic and fetch-with-JSON remains the documented
172
+ * fallback carrier.
173
+ *
174
+ * Every delivery problem — stub construction, RPC rejection, overload, deploy-in-progress —
175
+ * surfaces as `PortTransportError` (preserving the platform stub's own `retryable` signal
176
+ * when present) and NEVER as a fabricated answer: on `resolveAdmission` the routing layer
177
+ * turns exactly this error into `AdmissionIndeterminate` (SUB-031).
178
+ */
179
+ const threadPortTransportLayer = Layer.effect(ThreadPortTransport)(Effect.gen(function* () {
180
+ const namespace = yield* ThreadObjectNamespace;
181
+ return ThreadPortTransport.of({ call: (threadId, request) => callThreadObject(threadId, (target) => target.portCall(request), (cause) => portTransportFailure(threadId, cause)).pipe(Effect.provideService(ThreadObjectNamespace, namespace), Effect.withSpan("CloudflarePortTransport.call", { attributes: { threadId } })) });
182
+ }));
183
+ //#endregion
184
+ //#region src/internal/layers.ts
185
+ /**
186
+ * Owner-side execution port for a `portCall` request the wire endpoint has already decoded.
187
+ * It executes against THIS Object's LOCAL port facets — never the routed decorators, so a
188
+ * request cannot bounce between Objects — and returns the typed response for the endpoint to
189
+ * encode.
190
+ */
191
+ var ThreadObjectPorts = class extends Context.Service()("@effect-agent/platform-cloudflare/ThreadObjectPorts") {};
192
+ const decodeConfigValue = Schema.decodeUnknownEffect(CloudflareDurableRuntimeConfigValue);
193
+ const decodeThreadId = Schema.decodeUnknownEffect(ThreadId);
194
+ const decodeProducerId = Schema.decodeUnknownEffect(ProducerId);
195
+ const configFromOptions = (options) => decodeConfigValue({
196
+ deploymentId: options.deploymentId,
197
+ producerPrefix: options.producerPrefix,
198
+ ownershipLeaseDuration: options.ownershipLeaseDuration ?? CLOUDFLARE_RUNTIME_DEFAULTS.ownershipLeaseDuration,
199
+ alarmBackoffBase: options.alarmBackoffBase ?? CLOUDFLARE_RUNTIME_DEFAULTS.alarmBackoffBase,
200
+ alarmBackoffCap: options.alarmBackoffCap ?? CLOUDFLARE_RUNTIME_DEFAULTS.alarmBackoffCap,
201
+ wakeScanInterval: options.wakeScanInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.wakeScanInterval,
202
+ settlementPollInterval: options.settlementPollInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.settlementPollInterval,
203
+ leaseRenewalInterval: options.leaseRenewalInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.leaseRenewalInterval,
204
+ abortPollInterval: options.abortPollInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.abortPollInterval,
205
+ observationPollInterval: options.observationPollInterval ?? CLOUDFLARE_RUNTIME_DEFAULTS.observationPollInterval,
206
+ maxStoredValueBytes: options.maxStoredValueBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxStoredValueBytes,
207
+ verifyOnOpen: options.verifyOnOpen ?? CLOUDFLARE_RUNTIME_DEFAULTS.verifyOnOpen,
208
+ limits: {
209
+ maxQueueDepthPerLane: options.maxQueueDepthPerLane ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxQueueDepthPerLane,
210
+ maxInputBytes: Math.min(options.maxInputBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxInputBytes, options.maxStoredValueBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxStoredValueBytes),
211
+ maxDatabaseBytes: options.maxDatabaseBytes ?? CLOUDFLARE_RUNTIME_DEFAULTS.maxDatabaseBytes
212
+ }
213
+ }).pipe(Effect.mapError((error) => CloudflarePlatformConfigError.make({
214
+ message: `Invalid Cloudflare durable runtime configuration: ${error.message}`,
215
+ cause: error
216
+ })));
217
+ /**
218
+ * The Thread this Object owns, from the Object identity rule (plan §1.2): Thread
219
+ * Objects are addressed exclusively by `idFromName(threadId)`, so `ctx.id.name` IS the
220
+ * Thread ID. An unnamed Object (from `newUniqueId`) is a deployment error, not a lane.
221
+ */
222
+ const threadIdFromState = (ctx) => ctx.id.name === void 0 ? Effect.fail(CloudflarePlatformConfigError.make({ message: "This Durable Object was not created via idFromName(threadId); Thread Objects must be addressed by their Thread identity (plan §1.2)." })) : decodeThreadId(ctx.id.name).pipe(Effect.mapError((error) => CloudflarePlatformConfigError.make({
223
+ message: `The Durable Object name is not a valid ThreadId: ${error.message}`,
224
+ cause: error
225
+ })));
226
+ /**
227
+ * Validate deployment settings and derive services before building application dependencies.
228
+ * The native class factory builds this Layer inside its constructor gate. Custom Effect hosts
229
+ * can provide it around the complete application Layer with the native context already supplied.
230
+ */
231
+ const runtimeConfigLayer = (options, producerId) => Layer.unwrap(Effect.gen(function* () {
232
+ const { ctx } = yield* DurableObjectContext;
233
+ const config = yield* configFromOptions(options);
234
+ return Layer.mergeAll(Layer.succeed(CloudflareDurableRuntimeConfig, config), DurableRuntimeConfig.layer({
235
+ deploymentId: config.deploymentId,
236
+ producerId,
237
+ settlementPollInterval: Duration.millis(config.settlementPollInterval),
238
+ leaseRenewalInterval: Duration.millis(config.leaseRenewalInterval),
239
+ abortPollInterval: Duration.millis(config.abortPollInterval),
240
+ ...options.estimateCostMicrousd === void 0 ? {} : { estimateCostMicrousd: options.estimateCostMicrousd }
241
+ }), BrowserCrypto.layer, storageFailpointLayer({
242
+ storage: ctx.storage,
243
+ failpoint: options.storageFailpoint?.(ctx)
244
+ }), options.runtimeFailpoint === void 0 ? DurableRuntimeFailpoint.layer : Layer.succeed(DurableRuntimeFailpoint, { hit: options.runtimeFailpoint(ctx) }), options.maintenanceFailpoint === void 0 ? ThreadMaintenanceFailpoint.layer : Layer.succeed(ThreadMaintenanceFailpoint, { hit: options.maintenanceFailpoint(ctx) }), options.toolReconciler ?? ToolReconciler.uncertain, options.operationAuthorizer === void 0 ? Layer.empty : operationAuthorizerLayer(options.operationAuthorizer), options.toolFailureObserver === void 0 ? Layer.succeed(CurrentToolFailureObserver, void 0) : toolFailureObserverLayer(options.toolFailureObserver), RunContextPreparationPassthrough, RunToolAuthorization.allowAll);
245
+ }));
246
+ const producerIdentity = (prefix, owner) => decodeProducerId(`${prefix}:${owner}`).pipe(Effect.mapError((error) => CloudflarePlatformConfigError.make({
247
+ message: `The minted producer identity is invalid: ${error.message}`,
248
+ cause: error
249
+ })));
250
+ /** Native one-Thread configuration; retains its existing producer and logical identities. */
251
+ const layerConfig = (options) => Layer.unwrap(Effect.gen(function* () {
252
+ const { ctx } = yield* DurableObjectContext;
253
+ const threadId = yield* threadIdFromState(ctx);
254
+ const producerId = yield* producerIdentity(options.producerPrefix, threadId);
255
+ return Layer.mergeAll(runtimeConfigLayer(options, producerId), Layer.succeed(ThreadObjectIdentity, {
256
+ threadId,
257
+ producerId
258
+ }), Layer.succeed(ThreadObjectPlacement, { ownsThread: (target) => target === threadId }));
259
+ }));
260
+ /**
261
+ * Configuration for an application Object owning several logical Threads. The producer is
262
+ * the stable physical Object. No logical identity is installed globally: handleRpc binds and
263
+ * validates it per request using the placement guard and this actual runtime producer.
264
+ */
265
+ const layerHostConfig = (options, ownsThread) => Layer.unwrap(Effect.gen(function* () {
266
+ const { ctx } = yield* DurableObjectContext;
267
+ const producerId = yield* producerIdentity(options.producerPrefix, ctx.id.toString());
268
+ return Layer.merge(runtimeConfigLayer(options, producerId), Layer.succeed(ThreadObjectPlacement, { ownsThread }));
269
+ }));
270
+ /**
271
+ * Register typed Agents and version declarations. Hashing and dependency capture happen in
272
+ * this Layer's Scope, after application Layers have been provided. Every Agent's instruction,
273
+ * Tool, Schema, and model requirements remain visible until satisfied by Layer composition.
274
+ * Use Layer.unwrap for registration values that need effectful application setup.
275
+ */
276
+ const registeredLayer = (registrations, options = {}) => Layer.unwrap(Effect.map(compileRegistrations(registrations), (bindings) => boundLayer(bindings, options)));
277
+ function layer(registrations, options = {}) {
278
+ return registeredLayer(registrations, options);
279
+ }
280
+ /**
281
+ * Assemble the durable runtime from already-resolved Agent Bindings.
282
+ * Use `ThreadObject.layer` to compile typed Agent registrations instead.
283
+ * Supply host services through `ThreadObject.make` or `ThreadObject.layerConfig` and
284
+ * the Durable Object context and namespace Layers when composing a custom host.
285
+ */
286
+ const boundLayer = (bindings, options = {}) => Layer.unwrap(Effect.map(DurableObjectContext, ({ ctx }) => sharedLayer(DurableAgentRuntime.layerWithBindings(bindings), options).pipe(Layer.provideMerge(SqliteClient.layer({ storage: ctx.storage })))));
287
+ function layerInHost(application, options = {}) {
288
+ return sharedLayer(application, options);
289
+ }
290
+ const sharedLayer = (application, options = {}) => Layer.unwrap(Effect.gen(function* () {
291
+ const { ctx } = yield* DurableObjectContext;
292
+ const config = yield* CloudflareDurableRuntimeConfig;
293
+ const { ownsThread } = yield* ThreadObjectPlacement;
294
+ const storageOptions = {
295
+ storage: ctx.storage,
296
+ observationPollInterval: config.observationPollInterval,
297
+ ownershipLeaseDuration: config.ownershipLeaseDuration,
298
+ maxStoredValueBytes: config.maxStoredValueBytes,
299
+ verifyOnOpen: config.verifyOnOpen
300
+ };
301
+ const infrastructure = Layer.mergeAll(storageConfigLayer(storageOptions), Layer.effect(SqlClient)(SqlClient));
302
+ const rawLocalPorts = Layer.mergeAll(threadStoreLayer, submissionLedgerLayer).pipe(Layer.provide(infrastructure));
303
+ const base = Layer.mergeAll(DurableAlarmService.layer, ProgressWaitRegistry.layer);
304
+ const wakes = cloudflareWakeSchedulerLayer.pipe(Layer.provide(base));
305
+ const messageStore = guardedMessageDeliveryStoreLayer.pipe(Layer.provide(doMessageDeliveryStoreLayer().pipe(Layer.provide(infrastructure))), Layer.provide(wakes));
306
+ const messageRecovery = threadMessageDeliveryLayer.pipe(Layer.provide(MessageDeliveryDriver.layer({
307
+ batchSize: 4,
308
+ concurrency: 4
309
+ })), Layer.provide(cloudflarePreparedInputAdmissionLayer), Layer.provide(CloudflareThreadClient.layer), Layer.provide(messageStore), Layer.provide(wakes));
310
+ const publication = (options.publication ?? ThreadPublication.layer).pipe(Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))));
311
+ const projection = (options.projection ?? ThreadProjectionMaintenance.layer).pipe(Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))));
312
+ const localPorts = options.publication === void 0 && options.projection === void 0 ? rawLocalPorts : Layer.effectContext(Effect.gen(function* () {
313
+ const store = yield* ThreadStore;
314
+ const ledger = yield* SubmissionLedger;
315
+ const mutations = yield* ThreadMutationGate;
316
+ const index = yield* ThreadProjectionMaintenance;
317
+ const publish = yield* Effect.context();
318
+ const afterCommit = publishCommitted.pipe(Effect.provide(publish));
319
+ const sourceCommits = yield* Semaphore.make(1);
320
+ const observedStore = ThreadStore.of({
321
+ ...store,
322
+ append: (request) => mutations.withMutation(sourceCommits.withPermit(store.append(request).pipe(Effect.tap((result) => index.applyCommitted(request, result).pipe(Effect.catchCause((cause) => Cause.hasInterrupts(cause) ? Effect.interrupt : Effect.logError("Thread projection deferred after source commit", cause)))))).pipe(Effect.tap(() => afterCommit))).pipe(Effect.catchTag("DurableAlarmError", (cause) => ThreadStoreError.make({
323
+ operation: "prearm publication append",
324
+ message: cause.message,
325
+ cause
326
+ })))
327
+ });
328
+ const observeIntent = (body) => mutations.withMutation(body.pipe(Effect.tap(() => afterCommit))).pipe(Effect.catchTag("DurableAlarmError", (cause) => LedgerError.make({
329
+ operation: "prearm publication intent",
330
+ message: "The publication generation could not be armed",
331
+ cause
332
+ })));
333
+ return Context.make(ThreadStore, observedStore).pipe(Context.add(SubmissionLedger, {
334
+ ...ledger,
335
+ recordApprovalDecision: (request) => observeIntent(ledger.recordApprovalDecision(request)),
336
+ requestAbort: (request) => observeIntent(ledger.requestAbort(request)),
337
+ recordUnknownResolution: (request) => observeIntent(ledger.recordUnknownResolution(request))
338
+ }));
339
+ })).pipe(Layer.provide(rawLocalPorts));
340
+ const portsEndpointLayer = Layer.effect(ThreadObjectPorts)(Effect.gen(function* () {
341
+ const local = yield* Effect.context();
342
+ const ledger = Context.get(local, SubmissionLedger);
343
+ return ThreadObjectPorts.of({
344
+ handle: (request) => executePortRequest(request).pipe(Effect.provide(local)),
345
+ lookupSubmission: (submissionId) => ledger.lookup(SubmissionLookupById.make({ submissionId }))
346
+ });
347
+ })).pipe(Layer.provide(localPorts));
348
+ const routedPorts = Layer.mergeAll(routedSubmissionLedgerLayer({ ownsThread }), routedThreadStoreLayer({ ownsThread })).pipe(Layer.provide(localPorts), Layer.provide(threadPortTransportLayer));
349
+ const runtimeStack = application.pipe(Layer.provide(cloudflarePreparedInputAdmissionLayer.pipe(Layer.provide(CloudflareThreadClient.layer))), Layer.provideMerge(messageStore), Layer.provideMerge(routedPorts), Layer.provideMerge(wakes), Layer.provideMerge(base), Layer.provideMerge(portsEndpointLayer));
350
+ return Layer.mergeAll(runtimeStack, ThreadMaintenance.layer.pipe(Layer.provide(runtimeStack), Layer.provide(messageRecovery)), portsEndpointLayer, messageStore, messageRecovery).pipe(Layer.provideMerge(publication), Layer.provideMerge(projection), Layer.provideMerge(ThreadMutationGate.layer), Layer.provideMerge(infrastructure));
351
+ }));
352
+ //#endregion
353
+ //#region src/ThreadObject.ts
354
+ var ThreadObject_exports = /* @__PURE__ */ __exportAll({
355
+ AdminExplainRequest: () => AdminExplainRequest,
356
+ AdminFailed: () => AdminFailed,
357
+ AdminFailure: () => AdminFailure,
358
+ AdminResponse: () => AdminResponse,
359
+ AdminVerifyRequest: () => AdminVerifyRequest,
360
+ ExplainedRecovery: () => ExplainedRecovery,
361
+ ObligationsScanned: () => ObligationsScanned,
362
+ RetryExecuted: () => RetryExecuted,
363
+ ThreadObjectPorts: () => ThreadObjectPorts,
364
+ ThreadRpcOperation: () => ThreadRpcOperation,
365
+ VerifiedIntegrity: () => VerifiedIntegrity,
366
+ decodeAdminExplainRequest: () => decodeAdminExplainRequest,
367
+ decodeAdminResponse: () => decodeAdminResponse,
368
+ decodeAdminVerifyRequest: () => decodeAdminVerifyRequest,
369
+ decodeObligationThresholds: () => decodeObligationThresholds,
370
+ decodeRetryCommand: () => decodeRetryCommand,
371
+ encodeAdminResponse: () => encodeAdminResponse,
372
+ handleRpc: () => handleRpc,
373
+ layer: () => layer,
374
+ layerConfig: () => layerConfig,
375
+ layerHostConfig: () => layerHostConfig,
376
+ layerInHost: () => layerInHost,
377
+ make: () => make,
378
+ portCall: () => portCall,
379
+ submit: () => submit
380
+ });
381
+ /** Classify only a decoded port request so new protocol members cannot bypass pre-arming. */
382
+ const isMutatingPortRequest = (request) => {
383
+ switch (request._tag) {
384
+ case "LedgerAdmit":
385
+ case "LedgerMarkReady":
386
+ case "LedgerRequestAbort":
387
+ case "LedgerRecordChildSettled":
388
+ case "StoreMaterialize":
389
+ case "StoreAppend": return true;
390
+ case "LedgerLookup":
391
+ case "LedgerResolveAdmission":
392
+ case "StoreReadPage":
393
+ case "StoreInspectTail":
394
+ case "StoreExport": return false;
395
+ }
396
+ return false;
397
+ };
398
+ /** The literal encoded `PortFailed(PortProtocolError)` fallback (same shape as WP2's). */
399
+ const encodedPortProtocolFailure = (message) => ({
400
+ _tag: "PortFailed",
401
+ failure: {
402
+ _tag: "PortProtocolError",
403
+ message: boundHostDiagnostic(message)
404
+ }
405
+ });
406
+ const protocolFailure = (context) => (error) => HostProtocolError.make({ message: boundHostDiagnostic(`${context}: ${error.message}`) });
407
+ /** Fold one endpoint's typed failures into the uniform `HostResponse` envelope. */
408
+ const respond = (effect) => effect.pipe(Effect.map((result) => result), Effect.catch((failure) => Effect.succeed(HostFailed.make({ failure }))));
409
+ /** Encode the response envelope; an unencodable response degrades to a protocol failure. */
410
+ const encodeResponse = (response) => encodeHostResponse(response).pipe(Effect.catch((error) => Effect.succeed({
411
+ _tag: "HostFailed",
412
+ failure: {
413
+ _tag: "HostProtocolError",
414
+ message: boundHostDiagnostic(`The host response could not be encoded: ${error.message}`)
415
+ }
416
+ })));
417
+ const utf8Bytes = (value) => new TextEncoder().encode(JSON.stringify(value)).length;
418
+ /**
419
+ * The admission-limits gate, BEFORE `runtime.submit` touches the ledger (exit gate
420
+ * "resource limits are checked before admission"; DEPLOY-007). A replayed idempotency key is
421
+ * exempt: its accepted-work obligation already exists, and returning the original Receipt
422
+ * consumes no new quota. Refusals are typed `AdmissionLimitExceeded` and nothing is written.
423
+ */
424
+ const gateAdmissionLimits = Effect.fn("ThreadObject.gateAdmissionLimits")(function* (threadId, request) {
425
+ const config = yield* CloudflareDurableRuntimeConfig;
426
+ const ledger = yield* SubmissionLedger;
427
+ const { ctx } = yield* DurableObjectContext;
428
+ const existing = yield* ledger.lookup(SubmissionLookupByKey.make({
429
+ threadId,
430
+ principal: request.principal,
431
+ idempotencyKey: request.idempotencyKey
432
+ }));
433
+ if (Option.isSome(existing)) return;
434
+ const inputBytes = utf8Bytes(request.inputPayload);
435
+ if (inputBytes > config.limits.maxInputBytes) return yield* AdmissionLimitExceeded.make({
436
+ limit: "input-bytes",
437
+ actual: inputBytes,
438
+ maximum: config.limits.maxInputBytes
439
+ });
440
+ const nonterminal = yield* ledger.scanNonterminal.pipe(Stream.filter((submission) => submission.threadId === threadId), Stream.runCollect);
441
+ if (nonterminal.length >= config.limits.maxQueueDepthPerLane) return yield* AdmissionLimitExceeded.make({
442
+ limit: "queue-depth",
443
+ actual: nonterminal.length,
444
+ maximum: config.limits.maxQueueDepthPerLane
445
+ });
446
+ const databaseBytes = yield* Effect.sync(() => ctx.storage.sql.databaseSize);
447
+ if (databaseBytes > config.limits.maxDatabaseBytes) return yield* AdmissionLimitExceeded.make({
448
+ limit: "database-bytes",
449
+ actual: databaseBytes,
450
+ maximum: config.limits.maxDatabaseBytes
451
+ });
452
+ });
453
+ /**
454
+ * The submit-capable projection of an Agent Binding on the OBJECT side: the input arrived
455
+ * already encoded through the real input schema on the Worker side (`client.ts`), so the
456
+ * Object admits the canonical `PersistedJson` payload as-is; the resolved Binding re-derives
457
+ * everything else from the stored `(agentId, agentDigests)` at claim time (SUB-023).
458
+ */
459
+ const passthroughSubmitAgent = (agentId) => ({ definition: {
460
+ id: agentId,
461
+ input: PersistedJson
462
+ } });
463
+ /** A physical owner may hold other Threads; an addressed request cannot act on their IDs. */
464
+ const lookupAddressedSubmission = Effect.fn("ThreadObject.lookupAddressedSubmission")(function* (submissionId) {
465
+ const { threadId } = yield* ThreadObjectIdentity;
466
+ const submission = yield* (yield* ThreadObjectPorts).lookupSubmission(submissionId);
467
+ if (Option.isSome(submission) && submission.value.threadId !== threadId) return yield* HostProtocolError.make({ message: "The Submission belongs to another Thread" });
468
+ return submission;
469
+ });
470
+ const requireSubmissionThread = Effect.fn("ThreadObject.requireSubmissionThread")(function* (submissionId) {
471
+ const submission = yield* lookupAddressedSubmission(submissionId);
472
+ if (Option.isNone(submission)) return yield* LedgerError.make({
473
+ operation: "addressed Submission lookup",
474
+ message: "The addressed Thread has no such Submission"
475
+ });
476
+ });
477
+ const requireReceiptThread = Effect.fn("ThreadObject.requireReceiptThread")(function* (threadId) {
478
+ if (threadId !== (yield* ThreadObjectIdentity).threadId) return yield* HostProtocolError.make({ message: "The Receipt belongs to another Thread" });
479
+ });
480
+ const requirePortThread = (request) => {
481
+ switch (request._tag) {
482
+ case "LedgerLookup": return request.request._tag === "SubmissionLookupById" ? lookupAddressedSubmission(request.request.submissionId).pipe(Effect.asVoid) : requireReceiptThread(request.request.threadId);
483
+ case "LedgerMarkReady":
484
+ case "LedgerRequestAbort": return requireSubmissionThread(request.request.submissionId);
485
+ case "LedgerRecordChildSettled": return requireSubmissionThread(request.request.parentSubmissionId);
486
+ case "LedgerAdmit":
487
+ case "LedgerResolveAdmission":
488
+ case "StoreMaterialize":
489
+ case "StoreAppend":
490
+ case "StoreReadPage":
491
+ case "StoreInspectTail":
492
+ case "StoreExport": return requireReceiptThread(request.request.threadId);
493
+ }
494
+ };
495
+ /**
496
+ * Admit an already Schema-decoded request to a logical Thread in this physical owner.
497
+ * Custom hosts validate local placement before calling this Effect and provide their same
498
+ * runtime/maintenance instances. The native endpoint uses this path too: queue limits,
499
+ * idempotent receipts and the pre-admission generation/alarm commit have one owner.
500
+ */
501
+ const submit = Effect.fn("ThreadObject.submit")(function* (threadId, request) {
502
+ if (!(yield* ThreadObjectPlacement).ownsThread(threadId)) return yield* HostProtocolError.make({ message: "The Thread belongs to another Object" });
503
+ const mutations = yield* ThreadMutationGate;
504
+ const runtime = yield* DurableAgentRuntime;
505
+ yield* gateAdmissionLimits(threadId, request);
506
+ return yield* mutations.withMutation(runtime.submit(passthroughSubmitAgent(request.agentId), request.inputPayload, {
507
+ threadId,
508
+ principal: request.principal,
509
+ idempotencyKey: request.idempotencyKey,
510
+ ...request.admissionGroup === void 0 ? {} : { admissionGroup: request.admissionGroup },
511
+ ...request.admissionFence === void 0 ? {} : { admissionFence: request.admissionFence },
512
+ ...request.workerAdmission === void 0 ? {} : { workerAdmission: request.workerAdmission },
513
+ ...request.messageAdmission === void 0 ? {} : { messageAdmission: request.messageAdmission },
514
+ definitions: request.definitions
515
+ }).pipe(Effect.tap(() => publishCommitted)));
516
+ });
517
+ const submitEndpoint = (encoded) => decodeSubmitRequest(encoded).pipe(Effect.mapError(protocolFailure("The submit request could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
518
+ const identity = yield* ThreadObjectIdentity;
519
+ const receipt = yield* submit(identity.threadId, request);
520
+ return SubmitSucceeded.make({ receipt });
521
+ })), respond, Effect.flatMap(encodeResponse));
522
+ const submissionStatusEndpoint = (encoded) => decodeReceipt(encoded).pipe(Effect.mapError(protocolFailure("The receipt could not be decoded")), Effect.flatMap((receipt) => Effect.gen(function* () {
523
+ yield* (yield* OperationAuthorizer).authorize(OperationAuthorizationRequest.make({
524
+ operation: "awaitSettlement",
525
+ threadId: receipt.threadId,
526
+ submissionId: receipt.submissionId
527
+ }));
528
+ yield* requireReceiptThread(receipt.threadId);
529
+ yield* requireSubmissionThread(receipt.submissionId);
530
+ const runtime = yield* DurableAgentRuntime;
531
+ return SubmissionStatusResponse.make({ status: yield* runtime.submissionStatus(receipt) });
532
+ })), respond, Effect.flatMap(encodeResponse));
533
+ const awaitSettlementEndpoint = (encoded) => decodeReceipt(encoded).pipe(Effect.mapError(protocolFailure("The receipt could not be decoded")), Effect.flatMap((receipt) => Effect.gen(function* () {
534
+ yield* (yield* OperationAuthorizer).authorize(OperationAuthorizationRequest.make({
535
+ operation: "awaitSettlement",
536
+ threadId: receipt.threadId,
537
+ submissionId: receipt.submissionId
538
+ }));
539
+ yield* requireReceiptThread(receipt.threadId);
540
+ yield* requireSubmissionThread(receipt.submissionId);
541
+ const settlement = yield* (yield* DurableAgentRuntime).awaitSettlement(receipt);
542
+ return SettlementReached.make({ settlement });
543
+ })), respond, Effect.flatMap(encodeResponse));
544
+ const awaitProgressEndpoint = (encoded) => decodeAwaitProgressRequest(encoded).pipe(Effect.mapError(protocolFailure("The progress request could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
545
+ const identity = yield* ThreadObjectIdentity;
546
+ const runtime = yield* DurableAgentRuntime;
547
+ const registry = yield* ProgressWaitRegistry;
548
+ yield* Effect.scoped(Effect.gen(function* () {
549
+ const cancelled = yield* registry.subscribe(JSON.stringify([identity.threadId, request.waiterId]));
550
+ yield* Effect.raceFirst(runtime.awaitProgress(identity.threadId, request.afterSequence), cancelled);
551
+ }));
552
+ return ProgressObserved.make();
553
+ })), respond, Effect.flatMap(encodeResponse));
554
+ const cancelProgressEndpoint = (encoded) => decodeCancelProgressRequest(encoded).pipe(Effect.mapError(protocolFailure("The progress cancellation could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
555
+ const identity = yield* ThreadObjectIdentity;
556
+ yield* (yield* ProgressWaitRegistry).cancel(JSON.stringify([identity.threadId, request.waiterId]));
557
+ return ProgressCancelled.make();
558
+ })), respond, Effect.flatMap(encodeResponse));
559
+ const observePageEndpoint = (encoded) => decodeObservePageRequest(encoded).pipe(Effect.mapError(protocolFailure("The observe request could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
560
+ const identity = yield* ThreadObjectIdentity;
561
+ const store = yield* ThreadStore;
562
+ yield* (yield* OperationAuthorizer).authorize(OperationAuthorizationRequest.make({
563
+ operation: "observe",
564
+ threadId: identity.threadId
565
+ }));
566
+ const records = yield* Stream.runCollect(store.read(ThreadRead.make({
567
+ threadId: identity.threadId,
568
+ ...request.afterSequence === void 0 ? {} : { afterSequence: request.afterSequence },
569
+ limit: request.limit
570
+ })));
571
+ return ObservedPage.make({ records: [...records] });
572
+ })), respond, Effect.flatMap(encodeResponse));
573
+ const abortEndpoint = (encoded) => decodeAbortCommand(encoded).pipe(Effect.mapError(protocolFailure("The abort command could not be decoded")), Effect.flatMap((command) => Effect.gen(function* () {
574
+ yield* (yield* OperationAuthorizer).authorize(OperationAuthorizationRequest.make({
575
+ operation: "abort",
576
+ submissionId: command.submissionId
577
+ }));
578
+ yield* requireSubmissionThread(command.submissionId);
579
+ const maintenance = yield* ThreadMaintenance;
580
+ const runtime = yield* DurableAgentRuntime;
581
+ const intent = yield* maintenance.withMutation(runtime.abort(command));
582
+ return AbortRecorded.make({ intent });
583
+ })), respond, Effect.flatMap(encodeResponse));
584
+ const resolveApprovalEndpoint = (encoded) => decodeApprovalDecisionCommand(encoded).pipe(Effect.mapError(protocolFailure("The approval command could not be decoded")), Effect.flatMap((command) => Effect.gen(function* () {
585
+ yield* (yield* OperationAuthorizer).authorize(OperationAuthorizationRequest.make({
586
+ operation: "resolveApproval",
587
+ submissionId: command.submissionId
588
+ }));
589
+ yield* requireSubmissionThread(command.submissionId);
590
+ const maintenance = yield* ThreadMaintenance;
591
+ const runtime = yield* DurableAgentRuntime;
592
+ const intent = yield* maintenance.withMutation(runtime.resolveApproval(command));
593
+ return ApprovalRecorded.make({ intent });
594
+ })), respond, Effect.flatMap(encodeResponse));
595
+ const resolveUnknownEndpoint = (encoded) => decodeUnknownResolutionCommand(encoded).pipe(Effect.mapError(protocolFailure("The resolution command could not be decoded")), Effect.flatMap((command) => Effect.gen(function* () {
596
+ yield* (yield* OperationAuthorizer).authorize(OperationAuthorizationRequest.make({
597
+ operation: "resolveUnknown",
598
+ submissionId: command.submissionId
599
+ }));
600
+ yield* requireSubmissionThread(command.submissionId);
601
+ const maintenance = yield* ThreadMaintenance;
602
+ const runtime = yield* DurableAgentRuntime;
603
+ const intent = yield* maintenance.withMutation(runtime.resolveUnknown(command));
604
+ return UnknownResolutionRecorded.make({ intent });
605
+ })), respond, Effect.flatMap(encodeResponse));
606
+ /** Explain one Submission (`submissionId` present) or every nonterminal lane member. */
607
+ var AdminExplainRequest = class extends Schema.Class("@effect-agent/platform-cloudflare/AdminExplainRequest")({ submissionId: Schema.optionalKey(SubmissionId) }) {};
608
+ /** Verify carries no parameters — the addressed Object IS the lane. */
609
+ var AdminVerifyRequest = class extends Schema.Class("@effect-agent/platform-cloudflare/AdminVerifyRequest")({}) {};
610
+ /** Every typed failure of the four admin entry points, plus the protocol's own errors. */
611
+ const AdminFailure = Schema.Union([
612
+ AdmissionPolicyError,
613
+ OperationDenied,
614
+ RetryRefused,
615
+ LedgerError,
616
+ RunJournalError,
617
+ DigestError,
618
+ OwnershipLost,
619
+ SettlementConflict,
620
+ ThreadStoreError,
621
+ ThreadNotMaterialized,
622
+ AppendConflict,
623
+ FenceRejected,
624
+ DurableRuntimeFailpointError,
625
+ DurableAlarmError,
626
+ HostProtocolError
627
+ ]);
628
+ var ExplainedRecovery = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/ExplainedRecovery")("ExplainedRecovery", { explanations: Schema.Array(RecoveryExplanation).check(Schema.isMaxLength(1024)) }) {};
629
+ var VerifiedIntegrity = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/VerifiedIntegrity")("VerifiedIntegrity", { report: IntegrityReport }) {};
630
+ var RetryExecuted = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/RetryExecuted")("RetryExecuted", { report: RecoveryReport }) {};
631
+ var ObligationsScanned = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/ObligationsScanned")("ObligationsScanned", { report: ObligationReport }) {};
632
+ /** The admin entry point failed TYPED on the Object; the failure re-decodes verbatim. */
633
+ var AdminFailed = class extends Schema.TaggedClass("@effect-agent/platform-cloudflare/AdminFailed")("AdminFailed", { failure: AdminFailure }) {};
634
+ /** The uniform answer of one admin entry point. Callers narrow by the tag their call implies. */
635
+ const AdminResponse = Schema.Union([
636
+ ExplainedRecovery,
637
+ VerifiedIntegrity,
638
+ RetryExecuted,
639
+ ObligationsScanned,
640
+ AdminFailed
641
+ ]);
642
+ const decodeAdminExplainRequest = Schema.decodeUnknownEffect(AdminExplainRequest);
643
+ const decodeAdminVerifyRequest = Schema.decodeUnknownEffect(AdminVerifyRequest);
644
+ const decodeRetryCommand = Schema.decodeUnknownEffect(RetryCommand);
645
+ const decodeObligationThresholds = Schema.decodeUnknownEffect(ObligationThresholds);
646
+ const encodeAdminResponse = Schema.encodeEffect(AdminResponse);
647
+ const decodeAdminResponse = Schema.decodeUnknownEffect(AdminResponse);
648
+ /** Fold one admin endpoint's typed failures into the uniform `AdminResponse` envelope. */
649
+ const respondAdmin = (effect) => effect.pipe(Effect.map((result) => result), Effect.catch((failure) => Effect.succeed(AdminFailed.make({ failure }))));
650
+ /** Encode the admin response envelope; an unencodable response degrades to a protocol failure. */
651
+ const encodeAdminResponseTotal = (response) => encodeAdminResponse(response).pipe(Effect.catch((error) => Effect.succeed({
652
+ _tag: "AdminFailed",
653
+ failure: {
654
+ _tag: "HostProtocolError",
655
+ message: boundHostDiagnostic(`The admin response could not be encoded: ${error.message}`)
656
+ }
657
+ })));
658
+ const explainEndpoint = (encoded) => decodeAdminExplainRequest(encoded).pipe(Effect.mapError(protocolFailure("The explain request could not be decoded")), Effect.flatMap((request) => Effect.gen(function* () {
659
+ const identity = yield* ThreadObjectIdentity;
660
+ const runtime = yield* DurableAgentRuntime;
661
+ const explanations = request.submissionId === void 0 ? yield* runtime.explainThread(identity.threadId) : [yield* runtime.explain(request.submissionId)];
662
+ return ExplainedRecovery.make({ explanations });
663
+ })), respondAdmin, Effect.flatMap(encodeAdminResponseTotal));
664
+ const verifyEndpoint = (encoded) => decodeAdminVerifyRequest(encoded).pipe(Effect.mapError(protocolFailure("The verify request could not be decoded")), Effect.flatMap(() => Effect.gen(function* () {
665
+ const identity = yield* ThreadObjectIdentity;
666
+ const report = yield* (yield* DurableAgentRuntime).verify(identity.threadId);
667
+ return VerifiedIntegrity.make({ report });
668
+ })), respondAdmin, Effect.flatMap(encodeAdminResponseTotal));
669
+ const retryEndpoint = (encoded) => decodeRetryCommand(encoded).pipe(Effect.mapError(protocolFailure("The retry command could not be decoded")), Effect.flatMap((command) => Effect.gen(function* () {
670
+ const maintenance = yield* ThreadMaintenance;
671
+ const runtime = yield* DurableAgentRuntime;
672
+ const report = yield* maintenance.withMutation(runtime.retry(command));
673
+ return RetryExecuted.make({ report });
674
+ })), respondAdmin, Effect.flatMap(encodeAdminResponseTotal));
675
+ const obligationsEndpoint = (encoded) => decodeObligationThresholds(encoded).pipe(Effect.mapError(protocolFailure("The obligation thresholds could not be decoded")), Effect.flatMap((thresholds) => Effect.gen(function* () {
676
+ const report = yield* (yield* DurableAgentRuntime).scanObligations(thresholds);
677
+ return ObligationsScanned.make({ report });
678
+ })), respondAdmin, Effect.flatMap(encodeAdminResponseTotal));
679
+ /**
680
+ * Owner-side `portCall`: wrap a mutating envelope in the same pre-armed generation protocol as
681
+ * public RPC (a routed mutation committed by THIS Object must already carry the alarm that will
682
+ * finish it), execute on the LOCAL facets (never the routed decorators), then arm an immediate
683
+ * alarm so the mutated lane is processed promptly. Protocol anomalies answer
684
+ * `PortFailed(PortProtocolError)`.
685
+ */
686
+ const encodePortResponseTotal = (response) => encodePortResponse(response).pipe(Effect.catch((error) => Effect.succeed(encodedPortProtocolFailure(`The port response could not be encoded: ${error.message}`))));
687
+ const portGuardFailure = (failure) => PortFailed.make({ failure: failure._tag === "LedgerError" ? failure : PortProtocolError.make({ message: "The port request is not for the addressed Thread" }) });
688
+ const portCall = (encoded) => Effect.gen(function* () {
689
+ const ports = yield* ThreadObjectPorts;
690
+ const maintenance = yield* ThreadMaintenance;
691
+ const alarm = yield* DurableAlarmService;
692
+ const decoded = yield* decodePortRequest(encoded).pipe(Effect.map((request) => ({
693
+ _tag: "success",
694
+ request
695
+ })), Effect.catch((error) => Effect.succeed({
696
+ _tag: "failure",
697
+ message: error.message
698
+ })));
699
+ if (decoded._tag === "failure") return encodedPortProtocolFailure(`The port request could not be decoded: ${decoded.message}`);
700
+ if (decoded.request._tag === "LedgerLookup" && decoded.request.request._tag === "SubmissionLookupById") {
701
+ const response = yield* lookupAddressedSubmission(decoded.request.request.submissionId).pipe(Effect.map((submission) => PortSucceeded.make({ result: LedgerLookupResult.make(Option.isSome(submission) ? { submission: submission.value } : {}) })), Effect.catch((failure) => Effect.succeed(portGuardFailure(failure))));
702
+ return yield* encodePortResponseTotal(response);
703
+ }
704
+ const identityCheck = yield* requirePortThread(decoded.request).pipe(Effect.result);
705
+ if (identityCheck._tag === "Failure") return yield* encodePortResponseTotal(portGuardFailure(identityCheck.failure));
706
+ const mutating = isMutatingPortRequest(decoded.request);
707
+ const handled = yield* (mutating ? maintenance.withMutation(ports.handle(decoded.request)) : ports.handle(decoded.request)).pipe(Effect.exit);
708
+ if (handled._tag === "Failure") return encodedPortProtocolFailure("The owner Object could not arm its maintenance alarm before the mutation.");
709
+ const response = yield* encodePortResponseTotal(handled.value);
710
+ if (mutating) yield* alarm.scheduleNow.pipe(Effect.catch((error) => Effect.logWarning("ThreadObject.portCall: immediate re-arm failed", error)));
711
+ return response;
712
+ });
713
+ const wakeEndpoint = Effect.gen(function* () {
714
+ const identity = yield* ThreadObjectIdentity;
715
+ yield* (yield* WakeScheduler).notify(identity.threadId);
716
+ });
717
+ /** The per-Thread wire operations supported by native and application-owned endpoints. */
718
+ const ThreadRpcOperation = Schema.Literals([
719
+ "submitEncoded",
720
+ "submissionStatusEncoded",
721
+ "awaitSettlementEncoded",
722
+ "awaitProgressEncoded",
723
+ "cancelProgressEncoded",
724
+ "observePage",
725
+ "abortEncoded",
726
+ "resolveApprovalEncoded",
727
+ "resolveUnknownEncoded",
728
+ "portCall",
729
+ "wake"
730
+ ]);
731
+ const threadRpc = {
732
+ submitEncoded: submitEndpoint,
733
+ submissionStatusEncoded: submissionStatusEndpoint,
734
+ awaitSettlementEncoded: awaitSettlementEndpoint,
735
+ awaitProgressEncoded: awaitProgressEndpoint,
736
+ cancelProgressEncoded: cancelProgressEndpoint,
737
+ observePage: observePageEndpoint,
738
+ abortEncoded: abortEndpoint,
739
+ resolveApprovalEncoded: resolveApprovalEndpoint,
740
+ resolveUnknownEncoded: resolveUnknownEndpoint,
741
+ portCall,
742
+ wake: () => wakeEndpoint
743
+ };
744
+ /**
745
+ * Bind an addressed request to its logical Thread while sharing one physical runtime. This
746
+ * validates local placement before invoking the same native handlers. Receipts, Submission
747
+ * commands and port envelopes must match this identity; progress cancellation is Thread-scoped.
748
+ * The producer comes from the actual runtime configuration, never from caller input. These
749
+ * guards supplement the existing current model/Tool and operation authorization policies.
750
+ */
751
+ const handleRpc = Effect.fn("ThreadObject.handleRpc")(function* (threadId, operation, encoded) {
752
+ if (!(yield* ThreadObjectPlacement).ownsThread(threadId)) return yield* HostProtocolError.make({ message: "The Thread belongs to another Object" });
753
+ const { producerId } = yield* DurableRuntimeConfig;
754
+ return yield* threadRpc[operation](encoded).pipe(Effect.provideService(ThreadObjectIdentity, {
755
+ threadId,
756
+ producerId
757
+ }));
758
+ });
759
+ const alarmEndpoint = Effect.gen(function* () {
760
+ yield* (yield* ThreadMaintenance).pass;
761
+ });
762
+ const gateEndpoint = Effect.gen(function* () {
763
+ yield* (yield* ThreadMaintenance).ensureAlarm;
764
+ });
765
+ /**
766
+ * Adapter from effect-cf's native Durable Object services to Effect Agent's existing platform
767
+ * ports. effect-cf owns the cached ManagedRuntime and supplies these values once per Object
768
+ * incarnation; the durable runtime continues to depend only on the narrow services below.
769
+ */
770
+ const effectCfPlatformLayer = (namespaceBinding, rpcTracing = false) => {
771
+ const context = Layer.effect(DurableObjectContext)(Effect.gen(function* () {
772
+ const state = yield* DurableObjectState.DurableObjectState;
773
+ const env = yield* WorkerEnvironment;
774
+ return DurableObjectContext.of({
775
+ ctx: state.raw,
776
+ env
777
+ });
778
+ }));
779
+ const namespace = Layer.effect(ThreadObjectNamespace)(Effect.gen(function* () {
780
+ const env = yield* WorkerEnvironment;
781
+ const binding = yield* threadNamespaceFromEnv(env, namespaceBinding);
782
+ return ThreadObjectNamespace.of({
783
+ get: (threadId) => binding.get(binding.idFromName(threadId)),
784
+ ...rpcTracing === true ? { rpcTracing: namespaceBinding } : {}
785
+ });
786
+ }));
787
+ return Layer.merge(context, namespace);
788
+ };
789
+ /**
790
+ * Export a composed application Layer as a native Durable Object class.
791
+ * Bootstrap services are provided to the whole graph before it acquires, so application Layers
792
+ * can yield effect-cf's WorkerEnvironment and DurableObjectState, derived identity, and Crypto.
793
+ * Effect Config reads scalar Worker vars and secrets through effect-cf's environment provider;
794
+ * WorkerEnvironment exposes resource bindings without a separate config Layer.
795
+ * Application dependencies remain visible until Layer.provide satisfies them. effect-cf owns the
796
+ * cached ManagedRuntime, native RPC methods, event scopes, and telemetry flushing.
797
+ * Initialization is local and bounded inside the constructor gate. Cloudflare eviction does not
798
+ * guarantee finalizers; put resources requiring timely release in scoped operations or eventLayer.
799
+ */
800
+ const make = (applicationLayer, options) => {
801
+ const application = applicationLayer.pipe(Layer.provideMerge(layerConfig(options)), Layer.provideMerge(effectCfPlatformLayer(options.namespaceBinding, options.rpcTracing)));
802
+ const runtime = Layer.effectContext(Effect.gen(function* () {
803
+ const state = yield* DurableObjectState.DurableObjectState;
804
+ const scope = yield* Effect.scope;
805
+ return yield* state.blockConcurrencyWhile(Effect.gen(function* () {
806
+ const services = yield* Layer.buildWithScope(application, scope);
807
+ yield* gateEndpoint.pipe(Effect.provide(services));
808
+ return services;
809
+ }));
810
+ }));
811
+ const rpc = {
812
+ ...threadRpc,
813
+ explainEncoded: (encoded) => explainEndpoint(encoded),
814
+ verifyEncoded: (encoded) => verifyEndpoint(encoded),
815
+ retryEncoded: (encoded) => retryEndpoint(encoded),
816
+ obligationsEncoded: (encoded) => obligationsEndpoint(encoded)
817
+ };
818
+ const EffectCfThreadObject = DurableObject.make(runtime, {
819
+ ...options.rpcTracing === true ? { rpcTracing: { service: options.namespaceBinding } } : {},
820
+ ...options.eventLayer === void 0 ? {} : { eventLayer: options.eventLayer },
821
+ initialize: Effect.void,
822
+ rpc,
823
+ alarm: () => alarmEndpoint
824
+ });
825
+ class ThreadObject extends EffectCfThreadObject {
826
+ alarm(alarmInfo) {
827
+ return super.alarm?.(alarmInfo);
828
+ }
829
+ }
830
+ return ThreadObject;
831
+ };
832
+ //#endregion
833
+ export { layer as C, layerInHost as E, ThreadObjectPorts as S, layerHostConfig as T, encodeAdminResponse as _, AdminVerifyRequest as a, portCall as b, RetryExecuted as c, VerifiedIntegrity as d, decodeAdminExplainRequest as f, decodeRetryCommand as g, decodeObligationThresholds as h, AdminResponse as i, ThreadObject_exports as l, decodeAdminVerifyRequest as m, AdminFailed as n, ExplainedRecovery as o, decodeAdminResponse as p, AdminFailure as r, ObligationsScanned as s, AdminExplainRequest as t, ThreadRpcOperation as u, handleRpc as v, layerConfig as w, submit as x, make as y };
834
+
835
+ //# sourceMappingURL=ThreadObject-DvRG0dDz.mjs.map