@effect-agent/platform-cloudflare 0.1.0-beta.14 → 0.1.0-beta.15
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 +99 -96
- package/dist/index.mjs +208 -90
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
- package/src/alarm.ts +330 -113
- package/src/conversation-object.ts +34 -35
- package/src/index.ts +3 -2
- package/src/layers.ts +21 -5
|
@@ -246,17 +246,15 @@ const submitEndpoint = (encoded: unknown): Effect.Effect<unknown, never, Endpoin
|
|
|
246
246
|
const maintenance = yield* ConversationMaintenance;
|
|
247
247
|
const runtime = yield* DurableAgentRuntime;
|
|
248
248
|
yield* gateAdmissionLimits(request);
|
|
249
|
-
// Alarm invariant: the alarm
|
|
250
|
-
|
|
251
|
-
const receipt = yield*
|
|
252
|
-
passthroughSubmitAgent(request.agentId),
|
|
253
|
-
request.inputPayload,
|
|
254
|
-
{
|
|
249
|
+
// Alarm invariant: the generation + alarm commit BEFORE the admission, and maintenance
|
|
250
|
+
// cannot acknowledge that generation until this mutation leaves its public RPC seam.
|
|
251
|
+
const receipt = yield* maintenance.withMutation(
|
|
252
|
+
runtime.submit(passthroughSubmitAgent(request.agentId), request.inputPayload, {
|
|
255
253
|
conversationId: identity.conversationId,
|
|
256
254
|
principal: request.principal,
|
|
257
255
|
idempotencyKey: request.idempotencyKey,
|
|
258
256
|
definitions: request.definitions,
|
|
259
|
-
},
|
|
257
|
+
}),
|
|
260
258
|
);
|
|
261
259
|
return SubmitSucceeded.make({ receipt });
|
|
262
260
|
}),
|
|
@@ -324,8 +322,7 @@ const abortEndpoint = (encoded: unknown): Effect.Effect<unknown, never, Endpoint
|
|
|
324
322
|
Effect.gen(function* () {
|
|
325
323
|
const maintenance = yield* ConversationMaintenance;
|
|
326
324
|
const runtime = yield* DurableAgentRuntime;
|
|
327
|
-
yield* maintenance.
|
|
328
|
-
const intent = yield* runtime.abort(command);
|
|
325
|
+
const intent = yield* maintenance.withMutation(runtime.abort(command));
|
|
329
326
|
return AbortRecorded.make({ intent });
|
|
330
327
|
}),
|
|
331
328
|
),
|
|
@@ -360,10 +357,11 @@ const resolveApprovalEndpoint = (
|
|
|
360
357
|
Effect.gen(function* () {
|
|
361
358
|
const maintenance = yield* ConversationMaintenance;
|
|
362
359
|
const runtime = yield* DurableAgentRuntime;
|
|
363
|
-
yield* maintenance.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
360
|
+
const intent = yield* maintenance.withMutation(
|
|
361
|
+
runtime
|
|
362
|
+
.resolveApproval(command)
|
|
363
|
+
.pipe(Effect.catchTag("OperationDenied", deniedToProtocolFailure)),
|
|
364
|
+
);
|
|
367
365
|
return ApprovalRecorded.make({ intent });
|
|
368
366
|
}),
|
|
369
367
|
),
|
|
@@ -380,10 +378,11 @@ const resolveUnknownEndpoint = (
|
|
|
380
378
|
Effect.gen(function* () {
|
|
381
379
|
const maintenance = yield* ConversationMaintenance;
|
|
382
380
|
const runtime = yield* DurableAgentRuntime;
|
|
383
|
-
yield* maintenance.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
381
|
+
const intent = yield* maintenance.withMutation(
|
|
382
|
+
runtime
|
|
383
|
+
.resolveUnknown(command)
|
|
384
|
+
.pipe(Effect.catchTag("OperationDenied", deniedToProtocolFailure)),
|
|
385
|
+
);
|
|
387
386
|
return UnknownResolutionRecorded.make({ intent });
|
|
388
387
|
}),
|
|
389
388
|
),
|
|
@@ -541,10 +540,8 @@ const retryEndpoint = (encoded: unknown): Effect.Effect<unknown, never, Endpoint
|
|
|
541
540
|
Effect.gen(function* () {
|
|
542
541
|
const maintenance = yield* ConversationMaintenance;
|
|
543
542
|
const runtime = yield* DurableAgentRuntime;
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
yield* maintenance.preArm;
|
|
547
|
-
const report = yield* runtime.retry(command);
|
|
543
|
+
// Retry may repair durable state, so its generation + alarm commit before the mutation.
|
|
544
|
+
const report = yield* maintenance.withMutation(runtime.retry(command));
|
|
548
545
|
return RetryExecuted.make({ report });
|
|
549
546
|
}),
|
|
550
547
|
),
|
|
@@ -567,10 +564,11 @@ const obligationsEndpoint = (encoded: unknown): Effect.Effect<unknown, never, En
|
|
|
567
564
|
);
|
|
568
565
|
|
|
569
566
|
/**
|
|
570
|
-
* Owner-side `portCall`:
|
|
571
|
-
* THIS Object must already carry the alarm that will
|
|
572
|
-
* (never the routed decorators), then arm an immediate
|
|
573
|
-
* processed promptly. Protocol anomalies answer
|
|
567
|
+
* Owner-side `portCall`: wrap a mutating envelope in the same pre-armed generation protocol as
|
|
568
|
+
* public RPC (a routed mutation committed by THIS Object must already carry the alarm that will
|
|
569
|
+
* finish it), execute on the LOCAL facets (never the routed decorators), then arm an immediate
|
|
570
|
+
* alarm so the mutated lane is processed promptly. Protocol anomalies answer
|
|
571
|
+
* `PortFailed(PortProtocolError)`.
|
|
574
572
|
*/
|
|
575
573
|
const portCallEndpoint = (encoded: unknown): Effect.Effect<unknown, never, EndpointServices> =>
|
|
576
574
|
Effect.gen(function* () {
|
|
@@ -578,16 +576,17 @@ const portCallEndpoint = (encoded: unknown): Effect.Effect<unknown, never, Endpo
|
|
|
578
576
|
const maintenance = yield* ConversationMaintenance;
|
|
579
577
|
const alarm = yield* DurableAlarmService;
|
|
580
578
|
const mutating = isMutatingPortRequest(encoded);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
579
|
+
const handled = yield* (
|
|
580
|
+
mutating ? maintenance.withMutation(ports.handle(encoded)) : ports.handle(encoded)
|
|
581
|
+
).pipe(Effect.exit);
|
|
582
|
+
if (handled._tag === "Failure") {
|
|
583
|
+
// Without the committed generation/alarm the invariant cannot be promised; refuse before
|
|
584
|
+
// the port mutation runs. `ports.handle` itself is total, so this is the maintenance error.
|
|
585
|
+
return encodedPortProtocolFailure(
|
|
586
|
+
"The owner Object could not arm its maintenance alarm before the mutation.",
|
|
587
|
+
);
|
|
589
588
|
}
|
|
590
|
-
const response =
|
|
589
|
+
const response = handled.value;
|
|
591
590
|
if (mutating) {
|
|
592
591
|
// Prompt processing hint; the pre-armed alarm already guarantees convergence.
|
|
593
592
|
yield* alarm.scheduleNow.pipe(
|
|
@@ -612,7 +611,7 @@ const alarmEndpoint: Effect.Effect<void, MaintenancePassFailure, EndpointService
|
|
|
612
611
|
function* () {
|
|
613
612
|
const maintenance = yield* ConversationMaintenance;
|
|
614
613
|
// Typed pass failures propagate: the rejected promise makes workerd retry the alarm
|
|
615
|
-
// (at-least-once delivery), and the
|
|
614
|
+
// (at-least-once delivery), and the dirty generation retains a committed slot meanwhile.
|
|
616
615
|
yield* maintenance.pass;
|
|
617
616
|
},
|
|
618
617
|
);
|
package/src/index.ts
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* `makeConversationObjectClass` builds the class applications export from their Worker,
|
|
7
7
|
* `CloudflareDurableRuntime.layer` assembles the coordinator over the storage-cloudflare
|
|
8
8
|
* adapters and the WP2 cross-Object routing, `DurableAlarmService`/`ConversationMaintenance`
|
|
9
|
-
* multiplex every cadence into the Object's single alarm slot (
|
|
10
|
-
* committed alarm
|
|
9
|
+
* multiplex every cadence into the Object's single alarm slot (dirty or autonomously
|
|
10
|
+
* actionable work retains a committed alarm; stable external waits quiesce until their next
|
|
11
|
+
* durably pre-armed mutation), and
|
|
11
12
|
* `CloudflareConversationClient` is the Worker-side ingress. Platform bindings enter ONLY
|
|
12
13
|
* through the `bindings.ts` Layers (DEPLOY-010). This is the only workspace package allowed
|
|
13
14
|
* to import the `cloudflare:workers` runtime module.
|
package/src/layers.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { ConversationId } from "@effect-agent/core";
|
|
2
2
|
import {
|
|
3
3
|
AgentBindingResolver,
|
|
4
|
-
ConversationStore,
|
|
5
4
|
DurableAgentRuntime,
|
|
6
5
|
DurableRuntimeConfig,
|
|
7
6
|
DurableRuntimeFailpoint,
|
|
8
7
|
ProducerId,
|
|
9
|
-
SubmissionLedger,
|
|
10
8
|
ToolReconciler,
|
|
11
|
-
|
|
9
|
+
type ConversationStore,
|
|
12
10
|
type DurableRuntimeFailpointHandler,
|
|
13
11
|
type ResolvedBinding,
|
|
12
|
+
type SubmissionLedger,
|
|
13
|
+
type WakeScheduler,
|
|
14
14
|
} from "@effect-agent/session";
|
|
15
15
|
import {
|
|
16
16
|
conversationStoreLayer,
|
|
@@ -28,11 +28,16 @@ import { BrowserCrypto } from "@effect/platform-browser";
|
|
|
28
28
|
import { SqliteClient } from "@effect/sql-sqlite-do";
|
|
29
29
|
import { Context, Duration, Effect, Layer, Schema } from "effect";
|
|
30
30
|
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
ConversationMaintenance,
|
|
33
|
+
ConversationMaintenanceFailpoint,
|
|
34
|
+
DurableAlarmService,
|
|
35
|
+
type ConversationMaintenanceFailpointHandler,
|
|
36
|
+
} from "./alarm.ts";
|
|
32
37
|
import {
|
|
33
38
|
ConversationObjectIdentity,
|
|
34
|
-
ConversationObjectNamespace,
|
|
35
39
|
DurableObjectContext,
|
|
40
|
+
type ConversationObjectNamespace,
|
|
36
41
|
} from "./bindings.ts";
|
|
37
42
|
import {
|
|
38
43
|
CLOUDFLARE_RUNTIME_DEFAULTS,
|
|
@@ -89,6 +94,10 @@ export interface CloudflareDurableRuntimeOptions {
|
|
|
89
94
|
readonly runtimeFailpoint?:
|
|
90
95
|
| ((ctx: DurableObjectState) => DurableRuntimeFailpointHandler)
|
|
91
96
|
| undefined;
|
|
97
|
+
/** Conversation-maintenance generation/alarm fault injection; default none. */
|
|
98
|
+
readonly maintenanceFailpoint?:
|
|
99
|
+
| ((ctx: DurableObjectState) => ConversationMaintenanceFailpointHandler)
|
|
100
|
+
| undefined;
|
|
92
101
|
/**
|
|
93
102
|
* Reconciliation policy consulted for open ordinary Tool Calls before an Unknown Outcome
|
|
94
103
|
* is recorded (durability §10, DUR-009). Defaults to the fail-closed
|
|
@@ -340,6 +349,12 @@ export class CloudflareDurableRuntime {
|
|
|
340
349
|
options.runtimeFailpoint === undefined
|
|
341
350
|
? DurableRuntimeFailpoint.layer
|
|
342
351
|
: Layer.succeed(DurableRuntimeFailpoint)({ hit: options.runtimeFailpoint(ctx) });
|
|
352
|
+
const maintenanceFailpointLayer =
|
|
353
|
+
options.maintenanceFailpoint === undefined
|
|
354
|
+
? ConversationMaintenanceFailpoint.layer
|
|
355
|
+
: Layer.succeed(ConversationMaintenanceFailpoint)({
|
|
356
|
+
hit: options.maintenanceFailpoint(ctx),
|
|
357
|
+
});
|
|
343
358
|
const reconcilerLayer = options.toolReconciler ?? ToolReconciler.uncertain;
|
|
344
359
|
const bindingResolverLayer = Layer.effect(AgentBindingResolver)(
|
|
345
360
|
Effect.map(
|
|
@@ -352,6 +367,7 @@ export class CloudflareDurableRuntime {
|
|
|
352
367
|
identityLayer,
|
|
353
368
|
cloudflareConfigLayer,
|
|
354
369
|
DurableAlarmService.layer,
|
|
370
|
+
maintenanceFailpointLayer,
|
|
355
371
|
);
|
|
356
372
|
|
|
357
373
|
const runtimeStack = DurableAgentRuntime.layer.pipe(
|