@effect-agent/platform-cloudflare 0.0.1-beta.5 → 0.1.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +82 -34
- package/dist/index.mjs +609 -51
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -8
- package/src/code-mode-executor.ts +710 -0
- package/src/conversation-object.ts +146 -94
- package/src/index.ts +1 -0
|
@@ -29,14 +29,25 @@ import {
|
|
|
29
29
|
SubmissionLookupByKey,
|
|
30
30
|
type DurableSubmitAgent,
|
|
31
31
|
} from "@effect-agent/session";
|
|
32
|
-
import { DurableObject } from "cloudflare:workers";
|
|
33
|
-
import { Effect, Layer,
|
|
32
|
+
import type { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
|
|
33
|
+
import { Effect, Layer, Option, Schema, Stream } from "effect";
|
|
34
|
+
import {
|
|
35
|
+
DurableObject as EffectCfDurableObject,
|
|
36
|
+
DurableObjectState as EffectCfDurableObjectState,
|
|
37
|
+
WorkerEnvironment,
|
|
38
|
+
} from "effect-cf";
|
|
34
39
|
|
|
35
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
ConversationMaintenance,
|
|
42
|
+
DurableAlarmError,
|
|
43
|
+
DurableAlarmService,
|
|
44
|
+
type MaintenancePassFailure,
|
|
45
|
+
} from "./alarm.ts";
|
|
36
46
|
import {
|
|
37
47
|
ConversationObjectIdentity,
|
|
38
48
|
DurableObjectContext,
|
|
39
|
-
|
|
49
|
+
ConversationObjectNamespace,
|
|
50
|
+
conversationNamespaceFromEnv,
|
|
40
51
|
type CloudflareBindingError,
|
|
41
52
|
} from "./bindings.ts";
|
|
42
53
|
import {
|
|
@@ -69,7 +80,8 @@ import {
|
|
|
69
80
|
} from "./layers.ts";
|
|
70
81
|
|
|
71
82
|
/**
|
|
72
|
-
* `makeConversationObjectClass(options)` — the Conversation Durable Object
|
|
83
|
+
* `makeConversationObjectClass(options, observability?)` — the Conversation Durable Object
|
|
84
|
+
* (plan §1.4,
|
|
73
85
|
* D-P6-1): a factory returning a class that applications export from their Worker entry.
|
|
74
86
|
* One SQLite-backed Object per Conversation is the serialized owner (durability §6); the
|
|
75
87
|
* Object never runs `runResolvedWorker`'s infinite loop — each ingress event or alarm runs
|
|
@@ -95,9 +107,12 @@ export interface ConversationObjectOptions extends CloudflareDurableRuntimeOptio
|
|
|
95
107
|
readonly namespaceBinding: string;
|
|
96
108
|
}
|
|
97
109
|
|
|
98
|
-
type ConversationObjectError = CloudflareDurableRuntimeInitializationError | CloudflareBindingError;
|
|
99
|
-
|
|
100
110
|
type EndpointServices = CloudflareDurableRuntimeServices | DurableObjectContext;
|
|
111
|
+
type RuntimeServices = EndpointServices | ConversationObjectNamespace;
|
|
112
|
+
type ConversationObjectInitializationError =
|
|
113
|
+
| CloudflareDurableRuntimeInitializationError
|
|
114
|
+
| CloudflareBindingError
|
|
115
|
+
| MaintenancePassFailure;
|
|
101
116
|
|
|
102
117
|
/** Port envelope tags whose owner-side execution durably mutates this Object's lane. */
|
|
103
118
|
const MUTATING_PORT_TAGS: ReadonlySet<string> = new Set([
|
|
@@ -593,23 +608,56 @@ const wakeEndpoint: Effect.Effect<void, never, EndpointServices> = Effect.gen(fu
|
|
|
593
608
|
);
|
|
594
609
|
});
|
|
595
610
|
|
|
596
|
-
const alarmEndpoint: Effect.Effect<void,
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
611
|
+
const alarmEndpoint: Effect.Effect<void, MaintenancePassFailure, EndpointServices> = Effect.gen(
|
|
612
|
+
function* () {
|
|
613
|
+
const maintenance = yield* ConversationMaintenance;
|
|
614
|
+
// Typed pass failures propagate: the rejected promise makes workerd retry the alarm
|
|
615
|
+
// (at-least-once delivery), and the pass's own pre-arm keeps the slot committed meanwhile.
|
|
616
|
+
yield* maintenance.pass;
|
|
617
|
+
},
|
|
618
|
+
);
|
|
602
619
|
|
|
603
|
-
const gateEndpoint: Effect.Effect<void,
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
620
|
+
const gateEndpoint: Effect.Effect<void, MaintenancePassFailure, EndpointServices> = Effect.gen(
|
|
621
|
+
function* () {
|
|
622
|
+
// Forcing ConversationMaintenance forces the whole Layer stack: migration + exact-version
|
|
623
|
+
// check + configuration decode (DEPLOY-008 fails typed here, before any mutation), then
|
|
624
|
+
// the defensive local ensure-alarm half of the invariant. LOCAL-ONLY by construction.
|
|
625
|
+
const maintenance = yield* ConversationMaintenance;
|
|
626
|
+
yield* maintenance.ensureAlarm;
|
|
627
|
+
},
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Adapter from effect-cf's native Durable Object services to Effect Agent's existing platform
|
|
632
|
+
* ports. effect-cf owns the cached ManagedRuntime and supplies these values once per Object
|
|
633
|
+
* incarnation; the durable runtime continues to depend only on the narrow services below.
|
|
634
|
+
*/
|
|
635
|
+
const effectCfPlatformLayer = (
|
|
636
|
+
namespaceBinding: string,
|
|
637
|
+
): Layer.Layer<
|
|
638
|
+
DurableObjectContext | ConversationObjectNamespace,
|
|
639
|
+
CloudflareBindingError,
|
|
640
|
+
EffectCfDurableObjectState.DurableObjectState | WorkerEnvironment
|
|
641
|
+
> => {
|
|
642
|
+
const context = Layer.effect(DurableObjectContext)(
|
|
643
|
+
Effect.gen(function* () {
|
|
644
|
+
const state = yield* EffectCfDurableObjectState.DurableObjectState;
|
|
645
|
+
const env = yield* WorkerEnvironment;
|
|
646
|
+
return DurableObjectContext.of({ ctx: state.raw, env });
|
|
647
|
+
}),
|
|
648
|
+
);
|
|
649
|
+
const namespace = Layer.effect(ConversationObjectNamespace)(
|
|
650
|
+
Effect.gen(function* () {
|
|
651
|
+
const env = yield* WorkerEnvironment;
|
|
652
|
+
const binding = yield* conversationNamespaceFromEnv(env, namespaceBinding);
|
|
653
|
+
return ConversationObjectNamespace.of({ namespace: binding });
|
|
654
|
+
}),
|
|
655
|
+
);
|
|
656
|
+
return Layer.merge(context, namespace);
|
|
657
|
+
};
|
|
610
658
|
|
|
611
659
|
/** The public endpoint surface of one Conversation Object instance. */
|
|
612
|
-
export interface ConversationObjectInstance extends
|
|
660
|
+
export interface ConversationObjectInstance extends CloudflareDurableObject {
|
|
613
661
|
submitEncoded(encoded: unknown): Promise<unknown>;
|
|
614
662
|
awaitSettlementEncoded(encoded: unknown): Promise<unknown>;
|
|
615
663
|
observePage(encoded: unknown): Promise<unknown>;
|
|
@@ -622,7 +670,7 @@ export interface ConversationObjectInstance extends DurableObject {
|
|
|
622
670
|
obligationsEncoded(encoded: unknown): Promise<unknown>;
|
|
623
671
|
portCall(encoded: unknown): Promise<unknown>;
|
|
624
672
|
wake(): Promise<void>;
|
|
625
|
-
alarm(): Promise<void
|
|
673
|
+
alarm(alarmInfo?: AlarmInvocationInfo): Promise<void> | void;
|
|
626
674
|
}
|
|
627
675
|
|
|
628
676
|
/** The constructor shape workerd instantiates for each Conversation Object. */
|
|
@@ -631,84 +679,88 @@ export interface ConversationObjectClass {
|
|
|
631
679
|
}
|
|
632
680
|
|
|
633
681
|
/**
|
|
634
|
-
* Build the application's Conversation Object class (export it from the
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
*
|
|
682
|
+
* Build the application's Conversation Object class (export it from the Worker entry).
|
|
683
|
+
* effect-cf owns the cached ManagedRuntime, native RPC methods, event scopes, and post-handler
|
|
684
|
+
* OTLP flush scheduling for RPC and alarm events. The optional outer Layer is built per native
|
|
685
|
+
* event, so a host can install Tracer/Logger/Metric services and `OtlpExporter.Flusher` without
|
|
686
|
+
* Effect Agent owning exporter lifecycle machinery.
|
|
638
687
|
*/
|
|
639
|
-
export const makeConversationObjectClass = (
|
|
688
|
+
export const makeConversationObjectClass = <EventLayerError = never, EventServices = never>(
|
|
640
689
|
options: ConversationObjectOptions,
|
|
690
|
+
observability?: Layer.Layer<
|
|
691
|
+
EventServices,
|
|
692
|
+
EventLayerError,
|
|
693
|
+
| DurableObjectContext
|
|
694
|
+
| ConversationObjectNamespace
|
|
695
|
+
| EffectCfDurableObjectState.DurableObjectState
|
|
696
|
+
| WorkerEnvironment
|
|
697
|
+
>,
|
|
641
698
|
): ConversationObjectClass => {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
Layer.provideMerge(
|
|
650
|
-
Layer.mergeAll(
|
|
651
|
-
DurableObjectContext.layer(ctx, env),
|
|
652
|
-
conversationNamespaceLayer(env, options.namespaceBinding),
|
|
653
|
-
),
|
|
654
|
-
),
|
|
655
|
-
),
|
|
656
|
-
);
|
|
657
|
-
// The constructor gate: local-only checks; never the recovery pass (deadlock argument,
|
|
658
|
-
// plan §1.4). A failure here fails every delivery with the typed construction error.
|
|
659
|
-
ctx.blockConcurrencyWhile(() => this.#runtime.runPromise(gateEndpoint));
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
async submitEncoded(encoded: unknown): Promise<unknown> {
|
|
663
|
-
return this.#runtime.runPromise(submitEndpoint(encoded));
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
async awaitSettlementEncoded(encoded: unknown): Promise<unknown> {
|
|
667
|
-
return this.#runtime.runPromise(awaitSettlementEndpoint(encoded));
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
async observePage(encoded: unknown): Promise<unknown> {
|
|
671
|
-
return this.#runtime.runPromise(observePageEndpoint(encoded));
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
async abortEncoded(encoded: unknown): Promise<unknown> {
|
|
675
|
-
return this.#runtime.runPromise(abortEndpoint(encoded));
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
async resolveApprovalEncoded(encoded: unknown): Promise<unknown> {
|
|
679
|
-
return this.#runtime.runPromise(resolveApprovalEndpoint(encoded));
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
async resolveUnknownEncoded(encoded: unknown): Promise<unknown> {
|
|
683
|
-
return this.#runtime.runPromise(resolveUnknownEndpoint(encoded));
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
async explainEncoded(encoded: unknown): Promise<unknown> {
|
|
687
|
-
return this.#runtime.runPromise(explainEndpoint(encoded));
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
async verifyEncoded(encoded: unknown): Promise<unknown> {
|
|
691
|
-
return this.#runtime.runPromise(verifyEndpoint(encoded));
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
async retryEncoded(encoded: unknown): Promise<unknown> {
|
|
695
|
-
return this.#runtime.runPromise(retryEndpoint(encoded));
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
async obligationsEncoded(encoded: unknown): Promise<unknown> {
|
|
699
|
-
return this.#runtime.runPromise(obligationsEndpoint(encoded));
|
|
700
|
-
}
|
|
699
|
+
const application: Layer.Layer<
|
|
700
|
+
RuntimeServices,
|
|
701
|
+
CloudflareDurableRuntimeInitializationError | CloudflareBindingError,
|
|
702
|
+
EffectCfDurableObjectState.DurableObjectState | WorkerEnvironment
|
|
703
|
+
> = CloudflareDurableRuntime.layer(options).pipe(
|
|
704
|
+
Layer.provideMerge(effectCfPlatformLayer(options.namespaceBinding)),
|
|
705
|
+
);
|
|
701
706
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
707
|
+
// The storage/config Layer must acquire inside Cloudflare's constructor gate. effect-cf owns
|
|
708
|
+
// the ManagedRuntime, while this effectContext ensures its first Layer build enters the gate
|
|
709
|
+
// before migration, compatibility checks, or alarm inspection touch Object storage.
|
|
710
|
+
const runtime: Layer.Layer<
|
|
711
|
+
RuntimeServices,
|
|
712
|
+
ConversationObjectInitializationError,
|
|
713
|
+
EffectCfDurableObjectState.DurableObjectState | WorkerEnvironment
|
|
714
|
+
> = Layer.effectContext(
|
|
715
|
+
Effect.gen(function* () {
|
|
716
|
+
const state = yield* EffectCfDurableObjectState.DurableObjectState;
|
|
717
|
+
const scope = yield* Effect.scope;
|
|
718
|
+
return yield* state.blockConcurrencyWhile(
|
|
719
|
+
Effect.gen(function* () {
|
|
720
|
+
const services = yield* Layer.buildWithScope(application, scope);
|
|
721
|
+
yield* gateEndpoint.pipe(Effect.provide(services));
|
|
722
|
+
return services;
|
|
723
|
+
}),
|
|
724
|
+
);
|
|
725
|
+
}),
|
|
726
|
+
);
|
|
705
727
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
728
|
+
const rpc = {
|
|
729
|
+
submitEncoded: (encoded: unknown) => submitEndpoint(encoded),
|
|
730
|
+
awaitSettlementEncoded: (encoded: unknown) => awaitSettlementEndpoint(encoded),
|
|
731
|
+
observePage: (encoded: unknown) => observePageEndpoint(encoded),
|
|
732
|
+
abortEncoded: (encoded: unknown) => abortEndpoint(encoded),
|
|
733
|
+
resolveApprovalEncoded: (encoded: unknown) => resolveApprovalEndpoint(encoded),
|
|
734
|
+
resolveUnknownEncoded: (encoded: unknown) => resolveUnknownEndpoint(encoded),
|
|
735
|
+
explainEncoded: (encoded: unknown) => explainEndpoint(encoded),
|
|
736
|
+
verifyEncoded: (encoded: unknown) => verifyEndpoint(encoded),
|
|
737
|
+
retryEncoded: (encoded: unknown) => retryEndpoint(encoded),
|
|
738
|
+
obligationsEncoded: (encoded: unknown) => obligationsEndpoint(encoded),
|
|
739
|
+
portCall: (encoded: unknown) => portCallEndpoint(encoded),
|
|
740
|
+
wake: () => wakeEndpoint,
|
|
741
|
+
} satisfies EffectCfDurableObject.DurableObjectRpc<RuntimeServices | EventServices>;
|
|
742
|
+
|
|
743
|
+
const EffectCfConversationObject = EffectCfDurableObject.make<
|
|
744
|
+
RuntimeServices,
|
|
745
|
+
ConversationObjectInitializationError,
|
|
746
|
+
EventServices,
|
|
747
|
+
EventLayerError,
|
|
748
|
+
typeof rpc
|
|
749
|
+
>(runtime, {
|
|
750
|
+
...(observability === undefined ? {} : { eventLayer: observability }),
|
|
751
|
+
// Force the gated runtime Layer when Cloudflare loads this Object incarnation. Recovery stays
|
|
752
|
+
// in each bounded pass so cross-Object initialization cannot deadlock.
|
|
753
|
+
initialize: Effect.void,
|
|
754
|
+
rpc,
|
|
755
|
+
alarm: () => alarmEndpoint,
|
|
756
|
+
});
|
|
709
757
|
|
|
710
|
-
|
|
711
|
-
|
|
758
|
+
// effect-cf's class type keeps `alarm` optional even when the handler option is present. This
|
|
759
|
+
// concrete override reflects this factory's stronger contract while delegating execution to
|
|
760
|
+
// the effect-cf runtime unchanged.
|
|
761
|
+
class ConversationObject extends EffectCfConversationObject {
|
|
762
|
+
override alarm(alarmInfo?: AlarmInvocationInfo): Promise<void> | void {
|
|
763
|
+
return super.alarm?.(alarmInfo);
|
|
712
764
|
}
|
|
713
765
|
}
|
|
714
766
|
|
package/src/index.ts
CHANGED