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

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-7uipp6LW.mjs} +155 -117
  2. package/dist/Alarm-7uipp6LW.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-lTm6baDP.d.mts → CloudflareThreadClient-HO_3rkM7.d.mts} +362 -362
  8. package/dist/{CloudflareThreadClient-DU548EEj.mjs → CloudflareThreadClient-bA3lGA_2.mjs} +4 -4
  9. package/dist/CloudflareThreadClient-bA3lGA_2.mjs.map +1 -0
  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-SNcCsrS2.mjs} +52 -50
  14. package/dist/ThreadObject-SNcCsrS2.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-BvMiFR41.mjs} +2 -2
  21. package/dist/{prepared-admission-BoUPHSNQ.mjs.map → prepared-admission-BvMiFR41.mjs.map} +1 -1
  22. package/package.json +1 -1
  23. package/src/Alarm.ts +295 -201
  24. package/src/CloudflareThreadClient.ts +3 -2
  25. package/src/ThreadObject.ts +17 -22
  26. package/src/internal/layers.ts +124 -82
  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
@@ -386,13 +386,14 @@ const liveTextProtocolFailure = () =>
386
386
  message: "Provisional text is incomplete or invalid; discard drafts and read canonical records",
387
387
  });
388
388
 
389
+ const decodeLiveTextFrame = Schema.decodeUnknownSync(Schema.fromJsonString(LiveTextFrame));
390
+
389
391
  const decodeLiveText = (
390
392
  threadId: ThreadId,
391
393
  source: ReadableStream<unknown>,
392
394
  ): Stream.Stream<LiveTextFrame, HostProtocolError | ThreadClientError> =>
393
395
  Stream.suspend(() => {
394
396
  const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false });
395
- const decode = Schema.decodeUnknownSync(Schema.fromJsonString(LiveTextFrame));
396
397
  let pending = "";
397
398
  let pendingBytes = 0;
398
399
  let expected = 0;
@@ -488,7 +489,7 @@ const decodeLiveText = (
488
489
  if (newline < 0) break;
489
490
 
490
491
  const frame = yield* Effect.try({
491
- try: () => decode(pending),
492
+ try: () => decodeLiveTextFrame(pending),
492
493
  catch: liveTextProtocolFailure,
493
494
  });
494
495
 
@@ -351,16 +351,17 @@ const gateAdmissionLimits = Effect.fnUntraced(function* (
351
351
  );
352
352
  }
353
353
 
354
- const nonterminal = yield* ledger.scanNonterminal.pipe(
355
- Stream.filter((submission) => submission.threadId === threadId),
356
- Stream.runCollect,
354
+ const queueDepth = yield* Stream.runFold(
355
+ ledger.scanNonterminal,
356
+ () => 0,
357
+ (depth, submission) => depth + (submission.threadId === threadId ? 1 : 0),
357
358
  );
358
359
 
359
- if (nonterminal.length >= config.limits.maxQueueDepthPerLane) {
360
+ if (queueDepth >= config.limits.maxQueueDepthPerLane) {
360
361
  return yield* refuseFresh(
361
362
  AdmissionLimitExceeded.make({
362
363
  limit: "queue-depth",
363
- actual: nonterminal.length,
364
+ actual: queueDepth,
364
365
  maximum: config.limits.maxQueueDepthPerLane,
365
366
  }),
366
367
  );
@@ -1186,31 +1187,25 @@ const effectCfPlatformLayer = (
1186
1187
  DurableObjectContext | ThreadObjectNamespace,
1187
1188
  CloudflareBindingError,
1188
1189
  EffectCfDurableObjectState.DurableObjectState | WorkerEnvironment
1189
- > => {
1190
- const context = Layer.effect(DurableObjectContext)(
1190
+ > =>
1191
+ Layer.effectContext(
1191
1192
  Effect.gen(function* () {
1192
1193
  const state = yield* EffectCfDurableObjectState.DurableObjectState;
1193
1194
  const env = yield* WorkerEnvironment;
1194
-
1195
- return DurableObjectContext.of({ ctx: state.raw, env });
1196
- }),
1197
- );
1198
-
1199
- const namespace = Layer.effect(ThreadObjectNamespace)(
1200
- Effect.gen(function* () {
1201
- const env = yield* WorkerEnvironment;
1202
1195
  const binding = yield* threadNamespaceFromEnv(env, namespaceBinding);
1203
1196
 
1204
- return ThreadObjectNamespace.of({
1205
- get: (threadId) => binding.get(binding.idFromName(threadId)),
1206
- ...(rpcTracing === true ? { rpcTracing: namespaceBinding } : {}),
1207
- });
1197
+ return Context.make(DurableObjectContext, { ctx: state.raw, env }).pipe(
1198
+ Context.add(
1199
+ ThreadObjectNamespace,
1200
+ ThreadObjectNamespace.of({
1201
+ get: (threadId) => binding.get(binding.idFromName(threadId)),
1202
+ ...(rpcTracing === true ? { rpcTracing: namespaceBinding } : {}),
1203
+ }),
1204
+ ),
1205
+ );
1208
1206
  }),
1209
1207
  );
1210
1208
 
1211
- return Layer.merge(context, namespace);
1212
- };
1213
-
1214
1209
  /** The public endpoints and effect-cf invocation hook of one Thread Object instance. */
1215
1210
  export interface Instance<EventServices = never> extends InstanceType<
1216
1211
  EffectCfDurableObject.DurableObjectClass<
@@ -7,6 +7,7 @@ import {
7
7
  import { submissionLedgerLayer } from "@yielded/agent-storage-cloudflare/do-submission-ledger";
8
8
  import {
9
9
  threadStoreLayer,
10
+ journalLayer,
10
11
  storageConfigLayer,
11
12
  storageFailpointLayer,
12
13
  sqlOwnerLayer,
@@ -83,8 +84,15 @@ import {
83
84
  type WorkerStopCommand,
84
85
  } from "@yielded/agent/submission-ledger";
85
86
  import { ThreadProjectionMaintenance } from "@yielded/agent/thread-projection-maintenance";
86
- import type { ThreadReader } from "@yielded/agent/thread-store";
87
- import { AppendResult, ThreadStoreError, ThreadStore } from "@yielded/agent/thread-store";
87
+ import {
88
+ type ThreadReader,
89
+ AppendResult,
90
+ type AppendRequest,
91
+ type ThreadStoreFailure,
92
+ PreparedAppend,
93
+ ThreadStoreError,
94
+ ThreadStore,
95
+ } from "@yielded/agent/thread-store";
88
96
  import { ToolReconciler } from "@yielded/agent/tool-reconciler";
89
97
  import { type WakeScheduler } from "@yielded/agent/wake-scheduler";
90
98
  import {
@@ -134,7 +142,7 @@ import * as DueQueue from "./due-queue.ts";
134
142
  import { LiveTextHub } from "./live-text.ts";
135
143
  import {
136
144
  guardedMessageDeliveryStoreLayer,
137
- threadMessageDeliveryLayer,
145
+ lazyThreadMessageDeliveryLayer,
138
146
  } from "./message-delivery.ts";
139
147
  import { cloudflarePreparedInputAdmissionLayer } from "./prepared-admission.ts";
140
148
  import { ProgressWaitRegistry } from "./progress-wait.ts";
@@ -625,10 +633,14 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
625
633
  verifyOnOpen: config.verifyOnOpen,
626
634
  };
627
635
 
628
- const infrastructure = Layer.mergeAll(
629
- options.lifecyclePublication === undefined ? Layer.empty : lifecyclePublicationLayer,
630
- storageConfigLayer(storageOptions),
631
- Layer.effect(SqlClient)(SqlClient),
636
+ // Memoize these shared compositions within this Object's build, so repeated
637
+ // consumers reuse their contexts as well as the scoped leaf services.
638
+ const infrastructure = Layer.suspend(() =>
639
+ Layer.mergeAll(
640
+ options.lifecyclePublication === undefined ? Layer.empty : lifecyclePublicationLayer,
641
+ storageConfigLayer(storageOptions),
642
+ Layer.effect(SqlClient)(SqlClient),
643
+ ),
632
644
  );
633
645
 
634
646
  // Preserve the database owner's reader gate and rollback invalidators, while
@@ -661,7 +673,7 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
661
673
  }),
662
674
  ).pipe(Layer.provide(sqlOwnerLayer));
663
675
 
664
- const localInfrastructure = Layer.mergeAll(infrastructure, sourceOwner);
676
+ const localInfrastructure = Layer.suspend(() => Layer.mergeAll(infrastructure, sourceOwner));
665
677
 
666
678
  const sourceProgress = Layer.effect(SqlStorageProgress)(
667
679
  Effect.gen(function* () {
@@ -709,41 +721,58 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
709
721
 
710
722
  // The same local ports serve routed decorators and owner-side RPC execution.
711
723
  // 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),
724
+ const rawLocalPorts = Layer.suspend(() =>
725
+ Layer.mergeAll(threadStoreLayer, submissionLedgerLayer).pipe(
726
+ Layer.provide(journalLayer),
727
+ Layer.provide(localInfrastructure),
728
+ Layer.provide(sourceProgress),
729
+ ),
730
+ );
731
+
732
+ const base = Layer.suspend(() =>
733
+ Layer.mergeAll(DurableAlarmService.layer, ProgressWaitRegistry.layer),
715
734
  );
716
735
 
717
- const base = Layer.mergeAll(DurableAlarmService.layer, ProgressWaitRegistry.layer);
718
736
  const wakes = cloudflareWakeSchedulerLayer.pipe(Layer.provide(base));
719
737
 
720
- const messageStore = guardedMessageDeliveryStoreLayer.pipe(
721
- Layer.provide(
722
- doMessageDeliveryStoreLayer().pipe(
723
- Layer.provide(localInfrastructure),
724
- Layer.provide(sourceProgress),
738
+ const messageStore = Layer.suspend(() =>
739
+ guardedMessageDeliveryStoreLayer.pipe(
740
+ Layer.provide(
741
+ doMessageDeliveryStoreLayer().pipe(
742
+ Layer.provide(localInfrastructure),
743
+ Layer.provide(sourceProgress),
744
+ ),
725
745
  ),
746
+ Layer.provide(wakes),
726
747
  ),
727
- Layer.provide(wakes),
728
748
  );
729
749
 
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),
750
+ const messageRecovery = Layer.suspend(() =>
751
+ lazyThreadMessageDeliveryLayer(
752
+ // Four parallel attempts per wave. Native completion stops new wake-driven waves;
753
+ // retained retry deadlines schedule future alarms.
754
+ MessageDeliveryDriver.layer({ batchSize: 4, concurrency: 4 }).pipe(
755
+ Layer.provide(cloudflarePreparedInputAdmissionLayer),
756
+ Layer.provide(CloudflareThreadClient.layer),
757
+ Layer.provide(messageStore),
758
+ Layer.provide(wakes),
759
+ ),
760
+ ).pipe(Layer.provide(messageStore)),
738
761
  );
739
762
 
740
- const publication = (options.publication ?? ThreadPublication.layer).pipe(
741
- Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
742
- );
763
+ const publication =
764
+ options.publication === undefined
765
+ ? ThreadPublication.layer
766
+ : options.publication.pipe(
767
+ Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
768
+ );
743
769
 
744
- const projection = (options.projection ?? ThreadProjectionMaintenance.layer).pipe(
745
- Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
746
- );
770
+ const projection =
771
+ options.projection === undefined
772
+ ? ThreadProjectionMaintenance.layer
773
+ : options.projection.pipe(
774
+ Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
775
+ );
747
776
 
748
777
  const localPorts =
749
778
  options.publication === undefined &&
@@ -765,6 +794,54 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
765
794
  // Publication remains outside this local source/index critical section.
766
795
  const sourceCommits = yield* Semaphore.make(1);
767
796
 
797
+ const observeAppend = (
798
+ request: AppendRequest,
799
+ body: Effect.Effect<AppendResult, ThreadStoreFailure>,
800
+ ) =>
801
+ mutations
802
+ .withMutation(
803
+ sourceCommits
804
+ .withPermit(
805
+ body.pipe(
806
+ Effect.tap((result) =>
807
+ index
808
+ .applyCommitted(request, result)
809
+ .pipe(
810
+ Effect.catchCause((cause) =>
811
+ Cause.hasInterrupts(cause)
812
+ ? Effect.interrupt
813
+ : Effect.logError(
814
+ "Thread projection deferred after source commit",
815
+ cause,
816
+ ),
817
+ ),
818
+ ),
819
+ ),
820
+ ),
821
+ )
822
+ .pipe(Effect.tap(() => afterCommit)),
823
+ {
824
+ lanes: [
825
+ ...(options.projection === undefined ? [] : [DueQueue.Projection]),
826
+ ...(options.lifecyclePublication === undefined ||
827
+ !request.batch.records.some((record) =>
828
+ Schema.is(Schema.toType(LifecyclePublicationFact))(record.payload),
829
+ )
830
+ ? []
831
+ : [DueQueue.Lifecycle]),
832
+ ],
833
+ },
834
+ )
835
+ .pipe(
836
+ Effect.catchTag("DurableAlarmError", (cause) =>
837
+ ThreadStoreError.make({
838
+ operation: "prearm publication append",
839
+ message: cause.message,
840
+ cause,
841
+ }),
842
+ ),
843
+ );
844
+
768
845
  // Every runtime-owned producer prearms too: a crash between commit and invalidation
769
846
  // leaves a NEW, uncertified generation. Source errors keep their native port types.
770
847
  const observedStore = ThreadStore.of({
@@ -787,52 +864,7 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
787
864
  ),
788
865
  },
789
866
  }),
790
- append: (request) =>
791
- mutations
792
- .withMutation(
793
- sourceCommits
794
- .withPermit(
795
- store
796
- .append(request)
797
- .pipe(
798
- Effect.tap((result) =>
799
- index
800
- .applyCommitted(request, result)
801
- .pipe(
802
- Effect.catchCause((cause) =>
803
- Cause.hasInterrupts(cause)
804
- ? Effect.interrupt
805
- : Effect.logError(
806
- "Thread projection deferred after source commit",
807
- cause,
808
- ),
809
- ),
810
- ),
811
- ),
812
- ),
813
- )
814
- .pipe(Effect.tap(() => afterCommit)),
815
- {
816
- lanes: [
817
- ...(options.projection === undefined ? [] : [DueQueue.Projection]),
818
- ...(options.lifecyclePublication === undefined ||
819
- !request.batch.records.some((record) =>
820
- Schema.is(Schema.toType(LifecyclePublicationFact))(record.payload),
821
- )
822
- ? []
823
- : [DueQueue.Lifecycle]),
824
- ],
825
- },
826
- )
827
- .pipe(
828
- Effect.catchTag("DurableAlarmError", (cause) =>
829
- ThreadStoreError.make({
830
- operation: "prearm publication append",
831
- message: cause.message,
832
- cause,
833
- }),
834
- ),
835
- ),
867
+ append: (request) => observeAppend(request, store.append(request)),
836
868
  });
837
869
 
838
870
  const observeIntent = <A, Failure>(
@@ -886,6 +918,7 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
886
918
  );
887
919
 
888
920
  const stopWorker = ledger.stopWorker;
921
+ const appendInput = ledger.appendInput;
889
922
 
890
923
  return Context.make(ThreadStore, observedStore).pipe(
891
924
  Context.add(StorageOwner, storageOwner),
@@ -947,13 +980,22 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
947
980
  }),
948
981
  Context.add(SubmissionLedger, {
949
982
  ...ledger,
950
- ...(options.hostLanesForMutation === undefined
983
+ ...(appendInput === undefined
984
+ ? {}
985
+ : {
986
+ appendInput: (submissionId, input) =>
987
+ Effect.flatMap(PreparedAppend.capture(input), (request) =>
988
+ observeAppend(request, appendInput(submissionId, request)),
989
+ ),
990
+ }),
991
+ ...(options.hostLanesForMutation === undefined &&
992
+ options.lifecyclePublication === undefined
951
993
  ? {}
952
994
  : {
953
- admit: (request: AdmissionRequest) =>
995
+ admit: (...args: Parameters<SubmissionLedger["Service"]["admit"]>) =>
954
996
  observeMutation(
955
- { _tag: "Admission", request },
956
- ledger.admit(request),
997
+ { _tag: "Admission", request: args[0] },
998
+ ledger.admit(...args),
957
999
  false,
958
1000
  ),
959
1001
  }),
@@ -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
  );