@yielded/agent-platform-cloudflare 0.1.0-beta.174 → 0.1.0-beta.175

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 (30) hide show
  1. package/dist/{Alarm-4J9B8Fg8.mjs → Alarm-B44efvB1.mjs} +111 -74
  2. package/dist/Alarm-B44efvB1.mjs.map +1 -0
  3. package/dist/Alarm.d.mts +2 -0
  4. package/dist/Alarm.mjs +1 -1
  5. package/dist/CloudflareScheduling.mjs +2 -2
  6. package/dist/CloudflareSubscriptions.mjs +2 -2
  7. package/dist/{CloudflareThreadClient-DU548EEj.mjs → CloudflareThreadClient-Ai3MTs3X.mjs} +4 -4
  8. package/dist/CloudflareThreadClient-Ai3MTs3X.mjs.map +1 -0
  9. package/dist/{CloudflareThreadClient-lTm6baDP.d.mts → CloudflareThreadClient-HO_3rkM7.d.mts} +362 -362
  10. package/dist/CloudflareThreadClient.d.mts +1 -1
  11. package/dist/CloudflareThreadClient.mjs +1 -1
  12. package/dist/{ThreadObject-BLYgzcdT.d.mts → ThreadObject-CxNlhoPL.d.mts} +13 -13
  13. package/dist/{ThreadObject-C2LF3btz.mjs → ThreadObject-DGvlHWYi.mjs} +42 -43
  14. package/dist/ThreadObject-DGvlHWYi.mjs.map +1 -0
  15. package/dist/ThreadObject.d.mts +1 -1
  16. package/dist/ThreadObject.mjs +1 -1
  17. package/dist/WakeScheduler.mjs +1 -1
  18. package/dist/index.d.mts +2 -2
  19. package/dist/index.mjs +3 -3
  20. package/dist/{prepared-admission-BoUPHSNQ.mjs → prepared-admission-DYdffQ8K.mjs} +2 -2
  21. package/dist/{prepared-admission-BoUPHSNQ.mjs.map → prepared-admission-DYdffQ8K.mjs.map} +1 -1
  22. package/package.json +1 -1
  23. package/src/Alarm.ts +211 -116
  24. package/src/CloudflareThreadClient.ts +3 -2
  25. package/src/ThreadObject.ts +17 -22
  26. package/src/internal/layers.ts +55 -34
  27. package/src/internal/message-delivery.ts +61 -37
  28. package/dist/Alarm-4J9B8Fg8.mjs.map +0 -1
  29. package/dist/CloudflareThreadClient-DU548EEj.mjs.map +0 -1
  30. package/dist/ThreadObject-C2LF3btz.mjs.map +0 -1
@@ -134,7 +134,7 @@ import * as DueQueue from "./due-queue.ts";
134
134
  import { LiveTextHub } from "./live-text.ts";
135
135
  import {
136
136
  guardedMessageDeliveryStoreLayer,
137
- threadMessageDeliveryLayer,
137
+ lazyThreadMessageDeliveryLayer,
138
138
  } from "./message-delivery.ts";
139
139
  import { cloudflarePreparedInputAdmissionLayer } from "./prepared-admission.ts";
140
140
  import { ProgressWaitRegistry } from "./progress-wait.ts";
@@ -625,10 +625,14 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
625
625
  verifyOnOpen: config.verifyOnOpen,
626
626
  };
627
627
 
628
- const infrastructure = Layer.mergeAll(
629
- options.lifecyclePublication === undefined ? Layer.empty : lifecyclePublicationLayer,
630
- storageConfigLayer(storageOptions),
631
- Layer.effect(SqlClient)(SqlClient),
628
+ // Memoize these shared compositions within this Object's build, so repeated
629
+ // consumers reuse their contexts as well as the scoped leaf services.
630
+ const infrastructure = Layer.suspend(() =>
631
+ Layer.mergeAll(
632
+ options.lifecyclePublication === undefined ? Layer.empty : lifecyclePublicationLayer,
633
+ storageConfigLayer(storageOptions),
634
+ Layer.effect(SqlClient)(SqlClient),
635
+ ),
632
636
  );
633
637
 
634
638
  // Preserve the database owner's reader gate and rollback invalidators, while
@@ -661,7 +665,7 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
661
665
  }),
662
666
  ).pipe(Layer.provide(sqlOwnerLayer));
663
667
 
664
- const localInfrastructure = Layer.mergeAll(infrastructure, sourceOwner);
668
+ const localInfrastructure = Layer.suspend(() => Layer.mergeAll(infrastructure, sourceOwner));
665
669
 
666
670
  const sourceProgress = Layer.effect(SqlStorageProgress)(
667
671
  Effect.gen(function* () {
@@ -709,41 +713,57 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
709
713
 
710
714
  // The same local ports serve routed decorators and owner-side RPC execution.
711
715
  // The RPC executor must never receive routed ports and bounce requests between Objects.
712
- const rawLocalPorts = Layer.mergeAll(threadStoreLayer, submissionLedgerLayer).pipe(
713
- Layer.provide(localInfrastructure),
714
- Layer.provide(sourceProgress),
716
+ const rawLocalPorts = Layer.suspend(() =>
717
+ Layer.mergeAll(threadStoreLayer, submissionLedgerLayer).pipe(
718
+ Layer.provide(localInfrastructure),
719
+ Layer.provide(sourceProgress),
720
+ ),
721
+ );
722
+
723
+ const base = Layer.suspend(() =>
724
+ Layer.mergeAll(DurableAlarmService.layer, ProgressWaitRegistry.layer),
715
725
  );
716
726
 
717
- const base = Layer.mergeAll(DurableAlarmService.layer, ProgressWaitRegistry.layer);
718
727
  const wakes = cloudflareWakeSchedulerLayer.pipe(Layer.provide(base));
719
728
 
720
- const messageStore = guardedMessageDeliveryStoreLayer.pipe(
721
- Layer.provide(
722
- doMessageDeliveryStoreLayer().pipe(
723
- Layer.provide(localInfrastructure),
724
- Layer.provide(sourceProgress),
729
+ const messageStore = Layer.suspend(() =>
730
+ guardedMessageDeliveryStoreLayer.pipe(
731
+ Layer.provide(
732
+ doMessageDeliveryStoreLayer().pipe(
733
+ Layer.provide(localInfrastructure),
734
+ Layer.provide(sourceProgress),
735
+ ),
725
736
  ),
737
+ Layer.provide(wakes),
726
738
  ),
727
- Layer.provide(wakes),
728
739
  );
729
740
 
730
- const messageRecovery = threadMessageDeliveryLayer.pipe(
731
- // Four parallel attempts per wave. Native completion stops new wake-driven waves;
732
- // retained retry deadlines schedule future alarms.
733
- Layer.provide(MessageDeliveryDriver.layer({ batchSize: 4, concurrency: 4 })),
734
- Layer.provide(cloudflarePreparedInputAdmissionLayer),
735
- Layer.provide(CloudflareThreadClient.layer),
736
- Layer.provide(messageStore),
737
- Layer.provide(wakes),
741
+ const messageRecovery = Layer.suspend(() =>
742
+ lazyThreadMessageDeliveryLayer(
743
+ // Four parallel attempts per wave. Native completion stops new wake-driven waves;
744
+ // retained retry deadlines schedule future alarms.
745
+ MessageDeliveryDriver.layer({ batchSize: 4, concurrency: 4 }).pipe(
746
+ Layer.provide(cloudflarePreparedInputAdmissionLayer),
747
+ Layer.provide(CloudflareThreadClient.layer),
748
+ Layer.provide(messageStore),
749
+ Layer.provide(wakes),
750
+ ),
751
+ ).pipe(Layer.provide(messageStore)),
738
752
  );
739
753
 
740
- const publication = (options.publication ?? ThreadPublication.layer).pipe(
741
- Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
742
- );
754
+ const publication =
755
+ options.publication === undefined
756
+ ? ThreadPublication.layer
757
+ : options.publication.pipe(
758
+ Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
759
+ );
743
760
 
744
- const projection = (options.projection ?? ThreadProjectionMaintenance.layer).pipe(
745
- Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
746
- );
761
+ const projection =
762
+ options.projection === undefined
763
+ ? ThreadProjectionMaintenance.layer
764
+ : options.projection.pipe(
765
+ Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
766
+ );
747
767
 
748
768
  const localPorts =
749
769
  options.publication === undefined &&
@@ -947,13 +967,14 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
947
967
  }),
948
968
  Context.add(SubmissionLedger, {
949
969
  ...ledger,
950
- ...(options.hostLanesForMutation === undefined
970
+ ...(options.hostLanesForMutation === undefined &&
971
+ options.lifecyclePublication === undefined
951
972
  ? {}
952
973
  : {
953
- admit: (request: AdmissionRequest) =>
974
+ admit: (...args: Parameters<SubmissionLedger["Service"]["admit"]>) =>
954
975
  observeMutation(
955
- { _tag: "Admission", request },
956
- ledger.admit(request),
976
+ { _tag: "Admission", request: args[0] },
977
+ ledger.admit(...args),
957
978
  false,
958
979
  ),
959
980
  }),
@@ -5,7 +5,7 @@ import {
5
5
  MessageDeliveryStore,
6
6
  } from "@yielded/agent/message-delivery";
7
7
  import { WakeScheduler } from "@yielded/agent/wake-scheduler";
8
- import { Clock, Context, Effect, Layer, Option } from "effect";
8
+ import { Clock, Context, Duration, Effect, Exit, Layer, Option } from "effect";
9
9
 
10
10
  import { DurableAlarmError, ThreadMessageDelivery, ThreadMutationGate } from "../Alarm.ts";
11
11
  import { ThreadObjectPlacement } from "../CloudflareBindings.ts";
@@ -76,45 +76,69 @@ export const guardedMessageDeliveryStoreLayer = Layer.effect(
76
76
  }),
77
77
  );
78
78
 
79
- /** Message progress shares the native alarm slot without blocking source runtime work. */
80
- export const threadMessageDeliveryLayer = Layer.effectContext(
81
- Effect.gen(function* () {
82
- const driver = yield* MessageDeliveryDriver;
83
- const store = yield* MessageDeliveryStore;
79
+ /** Build the driver in the Object's Scope only when retained delivery work is due. */
80
+ export const lazyThreadMessageDeliveryLayer = <E, R>(
81
+ driver: Layer.Layer<MessageDeliveryDriver, E, R>,
82
+ ) =>
83
+ Layer.fromBuildMemo((memoMap, scope) =>
84
+ Effect.gen(function* () {
85
+ const context = yield* Effect.context<R>();
86
+ const store = yield* MessageDeliveryStore;
84
87
 
85
- const failure = (operation: string) => () =>
86
- DurableAlarmError.make({
87
- operation,
88
- message: "Durable message recovery remains pending",
89
- });
88
+ // Failed builds release their Layer scopes; let a later wave build again.
89
+ const service = yield* Effect.cachedWithTTL(
90
+ Layer.buildWithMemoMap(driver, memoMap, scope).pipe(
91
+ Effect.provide(context),
92
+ Effect.map(Context.get(MessageDeliveryDriver)),
93
+ Effect.mapError((cause) =>
94
+ DurableAlarmError.make({
95
+ operation: "prepare message delivery",
96
+ message: "Durable message recovery remains pending",
97
+ cause,
98
+ }),
99
+ ),
100
+ ),
101
+ (exit) => (Exit.isSuccess(exit) ? Duration.infinity : Duration.zero),
102
+ );
90
103
 
91
- const prepare = Effect.gen(function* () {
92
- const deadline = yield* store.nextDeadline();
104
+ const failure = (operation: string) => () =>
105
+ DurableAlarmError.make({
106
+ operation,
107
+ message: "Durable message recovery remains pending",
108
+ });
93
109
 
94
- if (deadline === null || deadline > (yield* Clock.currentTimeMillis))
95
- return { timeoutMillis: 1, run: Effect.succeed(Option.fromNullishOr(deadline)) };
96
- // The assembled driver has four permits: a wave is one parallel attempt window.
97
- const keys = yield* store.due(yield* Clock.currentTimeMillis, 4);
98
- const records = yield* Effect.forEach(keys, (key) => store.get(key));
110
+ const prepare = Effect.gen(function* () {
111
+ const deadline = yield* store.nextDeadline();
99
112
 
100
- return {
101
- timeoutMillis: Math.max(
102
- 1,
103
- ...records.map((record) => record?.policy.attemptTimeoutMillis ?? 1),
104
- ),
105
- run: Effect.forEach(keys, (key) => driver.process(key), {
106
- concurrency: 4,
107
- discard: true,
108
- }).pipe(
109
- Effect.andThen(store.nextDeadline()),
110
- Effect.map(Option.fromNullishOr),
111
- Effect.mapError(failure("dispatch message delivery")),
112
- ),
113
- };
114
- }).pipe(Effect.mapError(failure("prepare message delivery")));
113
+ if (deadline === null || deadline > (yield* Clock.currentTimeMillis))
114
+ return { timeoutMillis: 1, run: Effect.succeed(Option.fromNullishOr(deadline)) };
115
+ // The assembled driver has four permits: a wave is one parallel attempt window.
116
+ const keys = yield* store.due(yield* Clock.currentTimeMillis, 4);
117
+ const records = yield* Effect.forEach(keys, (key) => store.get(key));
115
118
 
116
- return Context.make(ThreadMessageDelivery, {
117
- prepare,
118
- });
119
- }),
119
+ return {
120
+ timeoutMillis: Math.max(
121
+ 1,
122
+ ...records.map((record) => record?.policy.attemptTimeoutMillis ?? 1),
123
+ ),
124
+ run: Effect.flatMap(service, (driver) =>
125
+ Effect.forEach(keys, (key) => driver.process(key), {
126
+ concurrency: 4,
127
+ discard: true,
128
+ }),
129
+ ).pipe(
130
+ Effect.andThen(store.nextDeadline()),
131
+ Effect.map(Option.fromNullishOr),
132
+ Effect.mapError(failure("dispatch message delivery")),
133
+ ),
134
+ };
135
+ }).pipe(Effect.mapError(failure("prepare message delivery")));
136
+
137
+ return Context.make(ThreadMessageDelivery, { prepare });
138
+ }),
139
+ );
140
+
141
+ /** Message progress shares the native alarm slot without blocking source runtime work. */
142
+ export const threadMessageDeliveryLayer = lazyThreadMessageDeliveryLayer(
143
+ Layer.effect(MessageDeliveryDriver, MessageDeliveryDriver),
120
144
  );