@effect-agent/platform-node 0.1.0-beta.44 → 0.1.0-beta.46

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.
@@ -1,4 +1,4 @@
1
- import type { SubmissionId } from "@effect-agent/core";
1
+ import { type SubmissionId } from "@effect-agent/core/Identifiers";
2
2
  import {
3
3
  CurrentToolFailureObserver,
4
4
  RunContextPreparationPassthrough,
@@ -7,38 +7,47 @@ import {
7
7
  type RunContextPreparation,
8
8
  type RunCostEstimator,
9
9
  type RunToolFailureObserver,
10
- } from "@effect-agent/engine";
10
+ } from "@effect-agent/engine/RunOptions";
11
+ import { scheduleStoreLayer } from "@effect-agent/storage-sqlite/SqliteScheduleStore";
11
12
  import {
12
- threadStoreLayer,
13
- scheduleStoreLayer,
14
13
  SqliteStorageConfig,
15
14
  SqliteStorageConfigValue,
15
+ } from "@effect-agent/storage-sqlite/SqliteStorageConfig";
16
+ import { type SqliteStorageFailpointHandler } from "@effect-agent/storage-sqlite/SqliteStorageFailpoint";
17
+ import { submissionLedgerLayer } from "@effect-agent/storage-sqlite/SqliteSubmissionLedger";
18
+ import {
19
+ threadStoreLayer,
16
20
  storageFailpointLayer,
17
- submissionLedgerLayer,
18
- type SqliteStorageFailpointHandler,
19
21
  type SqliteStorageInitializationError,
20
- } from "@effect-agent/storage-sqlite";
22
+ } from "@effect-agent/storage-sqlite/SqliteThreadStore";
23
+ import {
24
+ type AgentRegistration,
25
+ type ResolvedBinding,
26
+ } from "@effect-agent/thread/AgentRegistration";
21
27
  import {
22
- type ScheduleStore,
23
- type ThreadStore,
24
- type WakeScheduler,
25
- DEFAULT_OWNERSHIP_LEASE_DURATION,
26
- DeploymentId,
27
28
  DurableAgentRuntime,
28
29
  DurableRuntimeConfig,
30
+ } from "@effect-agent/thread/DurableAgentRuntime";
31
+ import {
29
32
  DurableRuntimeFailpoint,
30
- ProducerId,
33
+ type DurableRuntimeFailpointHandler,
34
+ } from "@effect-agent/thread/DurableFailpoint";
35
+ import { DeploymentId, ProducerId } from "@effect-agent/thread/Records";
36
+ import { type ScheduleStore } from "@effect-agent/thread/Schedule";
37
+ import {
38
+ DEFAULT_OWNERSHIP_LEASE_DURATION,
31
39
  ReleaseOwnershipRequest,
32
40
  SubmissionLedger,
33
- ToolReconciler,
34
- type DurableRuntimeFailpointHandler,
35
41
  type OwnershipToken,
36
- } from "@effect-agent/thread";
42
+ } from "@effect-agent/thread/SubmissionLedger";
43
+ import { type ThreadStore } from "@effect-agent/thread/ThreadStore";
44
+ import { ToolReconciler } from "@effect-agent/thread/ToolReconciler";
45
+ import { type WakeScheduler } from "@effect-agent/thread/WakeScheduler";
37
46
  import { NodeCrypto } from "@effect/platform-node";
38
47
  import { SqliteClient } from "@effect/sql-sqlite-node";
39
48
  import { Context, type Crypto, Duration, Effect, Layer, Ref, Schema } from "effect";
40
49
 
41
- import { NodeWakeSchedulerConfig, nodeWakeSchedulerLayer } from "./wake-scheduler.ts";
50
+ import { NodeWakeSchedulerConfig, nodeWakeSchedulerLayer } from "./NodeWakeScheduler.ts";
42
51
 
43
52
  const PositiveMillis = Schema.Int.check(Schema.isGreaterThan(0));
44
53
  const NonNegativeMillis = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
@@ -62,8 +71,8 @@ export class NodePlatformConfigError extends Schema.TaggedError<NodePlatformConf
62
71
  * construction, exposed as a typed service). Every cadence is in milliseconds and every bound is
63
72
  * finite; `workerConcurrency` caps how many worker loops `NodeDurableHost.runWorkers` drives.
64
73
  */
65
- export class NodeDurableRuntimeConfigValue extends Schema.Class<NodeDurableRuntimeConfigValue>(
66
- "@effect-agent/platform-node/NodeDurableRuntimeConfigValue",
74
+ export class NodeDurableAgentRuntimeConfigValue extends Schema.Class<NodeDurableAgentRuntimeConfigValue>(
75
+ "@effect-agent/platform-node/NodeDurableAgentRuntimeConfigValue",
67
76
  )({
68
77
  /** SQLite database file backing BOTH the Thread Log and the Submission Ledger. */
69
78
  filename: Schema.NonEmptyString,
@@ -90,17 +99,17 @@ export class NodeDurableRuntimeConfigValue extends Schema.Class<NodeDurableRunti
90
99
  }) {}
91
100
 
92
101
  /** Explicit configuration authority for the assembled Node durable runtime. */
93
- export class NodeDurableRuntimeConfig extends Context.Service<
94
- NodeDurableRuntimeConfig,
95
- NodeDurableRuntimeConfigValue
96
- >()("@effect-agent/platform-node/NodeDurableRuntimeConfig") {}
102
+ export class NodeDurableAgentRuntimeConfig extends Context.Service<
103
+ NodeDurableAgentRuntimeConfig,
104
+ NodeDurableAgentRuntimeConfigValue
105
+ >()("@effect-agent/platform-node/NodeDurableAgentRuntimeConfig") {}
97
106
 
98
107
  /**
99
- * Raw (unvalidated) construction options for `NodeDurableRuntime.layer`. Optional fields default
108
+ * Raw (unvalidated) construction options for `NodeDurableAgentRuntime.layer`. Optional fields default
100
109
  * to the documented production values; everything is schema-decoded into
101
- * `NodeDurableRuntimeConfigValue` before any resource opens (deployment §5 gate 1).
110
+ * `NodeDurableAgentRuntimeConfigValue` before any resource opens (deployment §5 gate 1).
102
111
  */
103
- export interface NodeDurableRuntimeOptions<
112
+ export interface NodeDurableAgentRuntimeOptions<
104
113
  ContextError = never,
105
114
  ContextRequirements = never,
106
115
  AuthorizationError = never,
@@ -161,25 +170,25 @@ export interface NodeDurableRuntimeOptions<
161
170
  }
162
171
 
163
172
  /** Built-in construction failures. `layer` also preserves supplied service Layers' errors. */
164
- export type NodeDurableRuntimeInitializationError =
173
+ export type NodeDurableAgentRuntimeInitializationError =
165
174
  | NodePlatformConfigError
166
175
  | SqliteStorageInitializationError;
167
176
 
168
- /** The services `NodeDurableRuntime.layer` provides. */
169
- export type NodeDurableRuntimeServices =
177
+ /** The services `NodeDurableAgentRuntime.layer` provides. */
178
+ export type NodeDurableAgentRuntimeServices =
170
179
  | DurableAgentRuntime
171
180
  | SubmissionLedger
172
181
  | ThreadStore
173
182
  | ScheduleStore
174
183
  | WakeScheduler
175
184
  | DurableRuntimeConfig
176
- | NodeDurableRuntimeConfig;
185
+ | NodeDurableAgentRuntimeConfig;
177
186
 
178
- const decodeConfigValue = Schema.decodeUnknownEffect(NodeDurableRuntimeConfigValue);
187
+ const decodeConfigValue = Schema.decodeUnknownEffect(NodeDurableAgentRuntimeConfigValue);
179
188
 
180
189
  const configFromOptions = (
181
- options: Omit<NodeDurableRuntimeOptions, "runContext" | "toolAuthorization">,
182
- ): Effect.Effect<NodeDurableRuntimeConfigValue, NodePlatformConfigError> =>
190
+ options: Omit<NodeDurableAgentRuntimeOptions, "runContext" | "toolAuthorization">,
191
+ ): Effect.Effect<NodeDurableAgentRuntimeConfigValue, NodePlatformConfigError> =>
183
192
  decodeConfigValue({
184
193
  filename: options.filename,
185
194
  deploymentId: options.deploymentId,
@@ -204,27 +213,30 @@ const configFromOptions = (
204
213
  );
205
214
 
206
215
  /** SQLite storage configuration derived from the single validated Node configuration. */
207
- const sqliteStorageConfigLayer: Layer.Layer<SqliteStorageConfig, never, NodeDurableRuntimeConfig> =
208
- Layer.effect(SqliteStorageConfig)(
209
- Effect.gen(function* () {
210
- const config = yield* NodeDurableRuntimeConfig;
211
-
212
- return SqliteStorageConfigValue.make({
213
- observationPollInterval: config.observationPollInterval,
214
- busyTimeout: config.busyTimeout,
215
- ownershipLeaseDuration: config.ownershipLeaseDuration,
216
- verifyOnOpen: config.verifyOnOpen,
217
- });
218
- }),
219
- );
216
+ const sqliteStorageConfigLayer: Layer.Layer<
217
+ SqliteStorageConfig,
218
+ never,
219
+ NodeDurableAgentRuntimeConfig
220
+ > = Layer.effect(SqliteStorageConfig)(
221
+ Effect.gen(function* () {
222
+ const config = yield* NodeDurableAgentRuntimeConfig;
223
+
224
+ return SqliteStorageConfigValue.make({
225
+ observationPollInterval: config.observationPollInterval,
226
+ busyTimeout: config.busyTimeout,
227
+ ownershipLeaseDuration: config.ownershipLeaseDuration,
228
+ verifyOnOpen: config.verifyOnOpen,
229
+ });
230
+ }),
231
+ );
220
232
 
221
233
  /** Thread coordinator configuration derived from the single validated Node configuration. */
222
234
  const durableRuntimeConfigLayer = (
223
235
  estimateCostMicrousd: RunCostEstimator | undefined,
224
- ): Layer.Layer<DurableRuntimeConfig, never, NodeDurableRuntimeConfig> =>
236
+ ): Layer.Layer<DurableRuntimeConfig, never, NodeDurableAgentRuntimeConfig> =>
225
237
  Layer.effect(DurableRuntimeConfig)(
226
238
  Effect.gen(function* () {
227
- const config = yield* NodeDurableRuntimeConfig;
239
+ const config = yield* NodeDurableAgentRuntimeConfig;
228
240
 
229
241
  return DurableRuntimeConfig.make({
230
242
  deploymentId: config.deploymentId,
@@ -241,10 +253,10 @@ const durableRuntimeConfigLayer = (
241
253
  const wakeSchedulerConfigLayer: Layer.Layer<
242
254
  NodeWakeSchedulerConfig,
243
255
  never,
244
- NodeDurableRuntimeConfig
256
+ NodeDurableAgentRuntimeConfig
245
257
  > = Layer.effect(NodeWakeSchedulerConfig)(
246
258
  Effect.gen(function* () {
247
- const config = yield* NodeDurableRuntimeConfig;
259
+ const config = yield* NodeDurableAgentRuntimeConfig;
248
260
 
249
261
  return { scanInterval: Duration.millis(config.wakeScanInterval) };
250
262
  }),
@@ -373,7 +385,7 @@ export const ownershipDrainLayer: Layer.Layer<SubmissionLedger, never, Submissio
373
385
  * verified during construction: an incompatible database file fails the Layer with
374
386
  * `SqliteStorageCompatibilityError` before anything is mutated (DEPLOY-008).
375
387
  */
376
- export class NodeDurableRuntime {
388
+ export class NodeDurableAgentRuntime {
377
389
  /** Validated configuration Layer; fails typed when the supplied options are out of bounds. */
378
390
  static configLayer<
379
391
  ContextError = never,
@@ -381,14 +393,14 @@ export class NodeDurableRuntime {
381
393
  AuthorizationError = never,
382
394
  AuthorizationRequirements = never,
383
395
  >(
384
- options: NodeDurableRuntimeOptions<
396
+ options: NodeDurableAgentRuntimeOptions<
385
397
  ContextError,
386
398
  ContextRequirements,
387
399
  AuthorizationError,
388
400
  AuthorizationRequirements
389
401
  >,
390
- ): Layer.Layer<NodeDurableRuntimeConfig, NodePlatformConfigError> {
391
- return Layer.effect(NodeDurableRuntimeConfig)(configFromOptions(options));
402
+ ): Layer.Layer<NodeDurableAgentRuntimeConfig, NodePlatformConfigError> {
403
+ return Layer.effect(NodeDurableAgentRuntimeConfig)(configFromOptions(options));
392
404
  }
393
405
 
394
406
  /** The full DN runtime stack over one SQLite file. */
@@ -398,20 +410,82 @@ export class NodeDurableRuntime {
398
410
  AuthorizationError = never,
399
411
  AuthorizationRequirements = never,
400
412
  >(
401
- options: NodeDurableRuntimeOptions<
413
+ options: NodeDurableAgentRuntimeOptions<
402
414
  ContextError,
403
415
  ContextRequirements,
404
416
  AuthorizationError,
405
417
  AuthorizationRequirements
406
418
  >,
407
419
  ): Layer.Layer<
408
- NodeDurableRuntimeServices,
409
- NodeDurableRuntimeInitializationError | ContextError | AuthorizationError,
420
+ NodeDurableAgentRuntimeServices,
421
+ NodeDurableAgentRuntimeInitializationError | ContextError | AuthorizationError,
410
422
  Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>
411
423
  > {
412
- return Layer.unwrap(
424
+ return NodeDurableAgentRuntime.assemble(DurableAgentRuntime.layerWithServices, options);
425
+ }
426
+
427
+ /** Own typed executable registrations for every worker using this Node runtime. */
428
+ static layerRegistered<
429
+ const Entries extends ReadonlyArray<AgentRegistration>,
430
+ ContextError = never,
431
+ ContextRequirements = never,
432
+ AuthorizationError = never,
433
+ AuthorizationRequirements = never,
434
+ >(
435
+ registrations: Entries,
436
+ options: NodeDurableAgentRuntimeOptions<
437
+ ContextError,
438
+ ContextRequirements,
439
+ AuthorizationError,
440
+ AuthorizationRequirements
441
+ >,
442
+ ) {
443
+ return NodeDurableAgentRuntime.assemble(
444
+ DurableAgentRuntime.layerRegistered(registrations),
445
+ options,
446
+ );
447
+ }
448
+
449
+ /** Construct from registrations already resolved within the enclosing application Scope. */
450
+ static layerWithBindings<
451
+ ContextError = never,
452
+ ContextRequirements = never,
453
+ AuthorizationError = never,
454
+ AuthorizationRequirements = never,
455
+ >(
456
+ bindings: ReadonlyArray<ResolvedBinding>,
457
+ options: NodeDurableAgentRuntimeOptions<
458
+ ContextError,
459
+ ContextRequirements,
460
+ AuthorizationError,
461
+ AuthorizationRequirements
462
+ >,
463
+ ) {
464
+ return NodeDurableAgentRuntime.assemble(
465
+ DurableAgentRuntime.layerWithBindings(bindings),
466
+ options,
467
+ );
468
+ }
469
+
470
+ private static assemble<
471
+ RuntimeError,
472
+ RuntimeRequirements,
473
+ ContextError,
474
+ ContextRequirements,
475
+ AuthorizationError,
476
+ AuthorizationRequirements,
477
+ >(
478
+ runtimeLayer: Layer.Layer<DurableAgentRuntime, RuntimeError, RuntimeRequirements>,
479
+ options: NodeDurableAgentRuntimeOptions<
480
+ ContextError,
481
+ ContextRequirements,
482
+ AuthorizationError,
483
+ AuthorizationRequirements
484
+ >,
485
+ ) {
486
+ const assembled = Layer.unwrap(
413
487
  Effect.map(configFromOptions(options), (config) => {
414
- const nodeConfigLayer = Layer.succeed(NodeDurableRuntimeConfig)(config);
488
+ const nodeConfigLayer = Layer.succeed(NodeDurableAgentRuntimeConfig)(config);
415
489
 
416
490
  const infrastructure = Layer.mergeAll(
417
491
  sqliteStorageConfigLayer,
@@ -443,7 +517,7 @@ export class NodeDurableRuntime {
443
517
  ),
444
518
  );
445
519
 
446
- return DurableAgentRuntime.layerWithServices.pipe(
520
+ return runtimeLayer.pipe(
447
521
  Layer.provideMerge(
448
522
  Layer.mergeAll(ports, durableRuntimeConfigLayer(options.estimateCostMicrousd)),
449
523
  ),
@@ -466,5 +540,13 @@ export class NodeDurableRuntime {
466
540
  );
467
541
  }),
468
542
  );
543
+
544
+ const runtime: Layer.Layer<
545
+ NodeDurableAgentRuntimeServices,
546
+ Layer.Error<typeof assembled>,
547
+ Layer.Services<typeof assembled>
548
+ > = assembled;
549
+
550
+ return runtime;
469
551
  }
470
552
  }
@@ -1,17 +1,20 @@
1
- import type { ThreadId, SubmissionId } from "@effect-agent/core";
1
+ import { type ThreadId, type SubmissionId } from "@effect-agent/core/Identifiers";
2
+ import {
3
+ type IntegrityReport,
4
+ type ObligationReport,
5
+ type ObligationThresholds,
6
+ type RecoveryExplanation,
7
+ type RetryCommand,
8
+ } from "@effect-agent/thread/Admin";
2
9
  import {
3
- compileRegistrations,
4
10
  type AgentRegistration,
11
+ type ResolvedBinding,
12
+ type DurableBindingFailure,
13
+ } from "@effect-agent/thread/AgentRegistration";
14
+ import {
5
15
  DurableAgentRuntime,
6
- type AbortCommand,
7
- type AbortIntent,
8
- type CanonicalRecordEnvelope,
9
- type ThreadNotMaterialized,
10
- type ThreadStoreError,
11
16
  type DurableAbortFailure,
12
17
  type DurableAwaitFailure,
13
- type ResolvedBinding,
14
- type DurableBindingFailure,
15
18
  type DurableExplainFailure,
16
19
  type DurableObserveOptions,
17
20
  type DurableObligationFailure,
@@ -21,26 +24,29 @@ import {
21
24
  type DurableSubmitOptions,
22
25
  type DurableVerifyFailure,
23
26
  type DurableWorkerFailure,
24
- type IntegrityReport,
25
- type ObligationReport,
26
- type ObligationThresholds,
27
- type OperationDenied,
28
27
  type Receipt,
29
- type RecoveryExplanation,
30
28
  type RecoveryReport,
31
- type RetryCommand,
29
+ } from "@effect-agent/thread/DurableAgentRuntime";
30
+ import { type OperationDenied } from "@effect-agent/thread/OperationAuthorizer";
31
+ import { type CanonicalRecordEnvelope } from "@effect-agent/thread/Records";
32
+ import {
33
+ type AbortCommand,
34
+ type AbortIntent,
32
35
  type Settlement,
33
- } from "@effect-agent/thread";
34
- import { NodeCrypto } from "@effect/platform-node";
36
+ } from "@effect-agent/thread/SubmissionLedger";
37
+ import {
38
+ type ThreadNotMaterialized,
39
+ type ThreadStoreError,
40
+ } from "@effect-agent/thread/ThreadStore";
35
41
  import { type Stream, Context, type Crypto, Effect, Layer, Ref, Schema } from "effect";
36
42
 
37
43
  import {
38
- NodeDurableRuntime,
39
- NodeDurableRuntimeConfig,
40
- type NodeDurableRuntimeInitializationError,
41
- type NodeDurableRuntimeOptions,
42
- type NodeDurableRuntimeServices,
43
- } from "./layers.ts";
44
+ NodeDurableAgentRuntime,
45
+ NodeDurableAgentRuntimeConfig,
46
+ type NodeDurableAgentRuntimeInitializationError,
47
+ type NodeDurableAgentRuntimeOptions,
48
+ type NodeDurableAgentRuntimeServices,
49
+ } from "./NodeDurableAgentRuntime.ts";
44
50
 
45
51
  /**
46
52
  * Admission is not open on this host: it is shutting down (deployment §6 step 1, DEPLOY-005).
@@ -50,75 +56,74 @@ export class AdmissionClosed extends Schema.TaggedError<AdmissionClosed>()("Admi
50
56
  message: Schema.String,
51
57
  }) {}
52
58
 
53
- const makeHost = (bindings: ReadonlyArray<ResolvedBinding>) =>
54
- Effect.gen(function* () {
55
- const runtime = yield* DurableAgentRuntime;
56
- const config = yield* NodeDurableRuntimeConfig;
59
+ const makeHost = Effect.gen(function* () {
60
+ const runtime = yield* DurableAgentRuntime;
61
+ const config = yield* NodeDurableAgentRuntimeConfig;
57
62
 
58
- // Startup gate (deployment §5, plan §host): configuration decoding and storage compatibility
59
- // already gated this Layer's dependencies; the last gate before admission opens is recovering
60
- // EVERY nonterminal Submission. Work needing a live Agent Binding is reported `deferred` and
61
- // stays a visible obligation for `runWorkers`; lanes durably blocked on an Unknown Outcome are
62
- // reported `unknown` and wait for the authorized `resolveUnknown` path (DUR-017) — they consume
63
- // no worker permit while the settlement obligation stays owed.
64
- const startupRecovery = yield* runtime.runRecovery;
63
+ // Startup gate (deployment §5, plan §host): configuration decoding and storage compatibility
64
+ // already gated this Layer's dependencies; the last gate before admission opens is recovering
65
+ // EVERY nonterminal Submission. Work needing a live Agent Binding is reported `deferred` and
66
+ // stays a visible obligation for `runWorkers`; lanes durably blocked on an Unknown Outcome are
67
+ // reported `unknown` and wait for the authorized `resolveUnknown` path (DUR-017) — they consume
68
+ // no worker permit while the settlement obligation stays owed.
69
+ const startupRecovery = yield* runtime.runRecovery;
65
70
 
66
- const admission = yield* Ref.make(true);
71
+ const admission = yield* Ref.make(true);
67
72
 
68
- // Shutdown step 1 (DEPLOY-005): admission closes before the ownership drain in the runtime
69
- // Layer below releases claims and before the stores close in reverse acquisition order.
70
- yield* Effect.addFinalizer(() => Ref.set(admission, false));
73
+ // Shutdown step 1 (DEPLOY-005): admission closes before the ownership drain in the runtime
74
+ // Layer below releases claims and before the stores close in reverse acquisition order.
75
+ yield* Effect.addFinalizer(() => Ref.set(admission, false));
71
76
 
72
- const requireAdmission: Effect.Effect<void, AdmissionClosed> = Ref.get(admission).pipe(
73
- Effect.flatMap((open) =>
74
- open
75
- ? Effect.void
76
- : Effect.fail(
77
- AdmissionClosed.make({ message: "The host is shutting down; admission is closed." }),
78
- ),
79
- ),
80
- );
77
+ const requireAdmission: Effect.Effect<void, AdmissionClosed> = Ref.get(admission).pipe(
78
+ Effect.flatMap((open) =>
79
+ open
80
+ ? Effect.void
81
+ : Effect.fail(
82
+ AdmissionClosed.make({ message: "The host is shutting down; admission is closed." }),
83
+ ),
84
+ ),
85
+ );
81
86
 
82
- const submit = <InputSchema extends Schema.Top>(
83
- agent: DurableSubmitAgent<InputSchema>,
84
- input: InputSchema["Type"],
85
- options: DurableSubmitOptions,
86
- ): Effect.Effect<
87
- Receipt,
88
- AdmissionClosed | DurableSubmitFailure,
89
- InputSchema["EncodingServices"]
90
- > => requireAdmission.pipe(Effect.andThen(runtime.submit(agent, input, options)));
87
+ const submit = <InputSchema extends Schema.Top>(
88
+ agent: DurableSubmitAgent<InputSchema>,
89
+ input: InputSchema["Type"],
90
+ options: DurableSubmitOptions,
91
+ ): Effect.Effect<
92
+ Receipt,
93
+ AdmissionClosed | DurableSubmitFailure,
94
+ InputSchema["EncodingServices"]
95
+ > => requireAdmission.pipe(Effect.andThen(runtime.submit(agent, input, options)));
91
96
 
92
- const runWorkers = <A, E, R>(worker: Effect.Effect<A, E, R>): Effect.Effect<void, E, R> =>
93
- Effect.forEach(
94
- Array.from({ length: config.workerConcurrency }, (_, index) => index),
95
- () => worker,
96
- { concurrency: "unbounded", discard: true },
97
- );
97
+ const runWorkers = <A, E, R>(worker: Effect.Effect<A, E, R>): Effect.Effect<void, E, R> =>
98
+ Effect.forEach(
99
+ Array.from({ length: config.workerConcurrency }, (_, index) => index),
100
+ () => worker,
101
+ { concurrency: "unbounded", discard: true },
102
+ );
98
103
 
99
- // S2 multi-binding pool: every claimed head resolves its exact registered Binding through
100
- // the host's exact registrations, so one bounded
101
- // pool serves parent and child lanes — the spec §12 smallest-pool suspension/wakeup proof
102
- // runs `workerConcurrency: 1` over exactly this loop.
103
- const runResolvedWorkers = runWorkers(runtime.runResolvedWorker(bindings));
104
+ // S2 multi-binding pool: every claimed head resolves its exact registered Binding through
105
+ // the host's exact registrations, so one bounded
106
+ // pool serves parent and child lanes — the spec §12 smallest-pool suspension/wakeup proof
107
+ // runs `workerConcurrency: 1` over exactly this loop.
108
+ const runResolvedWorkers = runWorkers(runtime.runResolvedWorker);
104
109
 
105
- return NodeDurableHost.of({
106
- startupRecovery,
107
- admissionOpen: Ref.get(admission),
108
- submit,
109
- awaitSettlement: runtime.awaitSettlement,
110
- observe: runtime.observe,
111
- abort: runtime.abort,
112
- explain: runtime.explain,
113
- explainThread: runtime.explainThread,
114
- verify: runtime.verify,
115
- retry: runtime.retry,
116
- wake: runtime.wake,
117
- scanObligations: runtime.scanObligations,
118
- runWorkers,
119
- runResolvedWorkers,
120
- });
110
+ return NodeDurableHost.of({
111
+ startupRecovery,
112
+ admissionOpen: Ref.get(admission),
113
+ submit,
114
+ awaitSettlement: runtime.awaitSettlement,
115
+ observe: runtime.observe,
116
+ abort: runtime.abort,
117
+ explain: runtime.explain,
118
+ explainThread: runtime.explainThread,
119
+ verify: runtime.verify,
120
+ retry: runtime.retry,
121
+ wake: runtime.wake,
122
+ scanObligations: runtime.scanObligations,
123
+ runWorkers,
124
+ runResolvedWorkers,
121
125
  });
126
+ });
122
127
 
123
128
  /**
124
129
  * Operational host lifecycle for the DN runtime (deployment §2/§5/§6).
@@ -210,57 +215,53 @@ export class NodeDurableHost extends Context.Service<
210
215
  AuthorizationRequirements = never,
211
216
  >(
212
217
  registrations: Entries,
213
- options: NodeDurableRuntimeOptions<
218
+ options: NodeDurableAgentRuntimeOptions<
214
219
  ContextError,
215
220
  ContextRequirements,
216
221
  AuthorizationError,
217
222
  AuthorizationRequirements
218
223
  >,
219
224
  ) {
220
- return Layer.unwrap(
221
- Effect.map(compileRegistrations(registrations), (bindings) =>
222
- NodeDurableHost.layerStack({ ...options, bindings }),
223
- ),
224
- ).pipe(Layer.provide(NodeCrypto.layer));
225
+ return NodeDurableHost.layer.pipe(
226
+ Layer.provideMerge(NodeDurableAgentRuntime.layerRegistered(registrations, options)),
227
+ );
225
228
  }
226
229
 
227
230
  /**
228
- * Host gates over an assembled `NodeDurableRuntime` stack. Bindings must carry the exact
229
- * digests stored by submitters. Omission registers no Agents, so resolved work fails closed.
231
+ * Host gates over an assembled `NodeDurableAgentRuntime` stack. The runtime Layer owns
232
+ * executable registrations; omission registers no Agents, so resolved work fails closed.
230
233
  */
231
- static readonly layer = (
232
- bindings: ReadonlyArray<ResolvedBinding> = [],
233
- ): Layer.Layer<
234
+ static readonly layer: Layer.Layer<
234
235
  NodeDurableHost,
235
236
  DurableWorkerFailure,
236
- DurableAgentRuntime | NodeDurableRuntimeConfig
237
- > => Layer.effect(NodeDurableHost)(makeHost(bindings));
237
+ DurableAgentRuntime | NodeDurableAgentRuntimeConfig
238
+ > = Layer.effect(NodeDurableHost)(makeHost);
238
239
 
239
- /** The complete DN host: `NodeDurableRuntime.layer(options)` plus the host lifecycle gates. */
240
+ /** The complete DN host: `NodeDurableAgentRuntime.layer(options)` plus the host lifecycle gates. */
240
241
  static layerStack<
241
242
  ContextError = never,
242
243
  ContextRequirements = never,
243
244
  AuthorizationError = never,
244
245
  AuthorizationRequirements = never,
245
246
  >(
246
- options: NodeDurableRuntimeOptions<
247
+ options: NodeDurableAgentRuntimeOptions<
247
248
  ContextError,
248
249
  ContextRequirements,
249
250
  AuthorizationError,
250
251
  AuthorizationRequirements
251
252
  > & { readonly bindings?: ReadonlyArray<ResolvedBinding> },
252
253
  ): Layer.Layer<
253
- NodeDurableHost | NodeDurableRuntimeServices,
254
+ NodeDurableHost | NodeDurableAgentRuntimeServices,
254
255
  | DurableWorkerFailure
255
- | NodeDurableRuntimeInitializationError
256
+ | NodeDurableAgentRuntimeInitializationError
256
257
  | ContextError
257
258
  | AuthorizationError,
258
259
  Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>
259
260
  > {
260
261
  const { bindings = [], ...runtimeOptions } = options;
261
262
 
262
- return NodeDurableHost.layer(bindings).pipe(
263
- Layer.provideMerge(NodeDurableRuntime.layer(runtimeOptions)),
263
+ return NodeDurableHost.layer.pipe(
264
+ Layer.provideMerge(NodeDurableAgentRuntime.layerWithBindings(bindings, runtimeOptions)),
264
265
  );
265
266
  }
266
267
  }
@@ -1,20 +1,22 @@
1
1
  import {
2
- type ScheduleProcessFailure,
3
2
  type ScheduleAuthorizer,
4
3
  type SchedulingLimits,
5
4
  ScheduleStorageError,
6
5
  ScheduleStore,
7
6
  type ScheduleValidationError,
8
7
  ScheduleWake,
8
+ defaultSchedulingLimits,
9
+ } from "@effect-agent/thread/Schedule";
10
+ import {
11
+ type ScheduleProcessFailure,
9
12
  Scheduling,
10
13
  ScheduleDriver,
11
- defaultSchedulingLimits,
12
- } from "@effect-agent/thread";
14
+ } from "@effect-agent/thread/Scheduling";
13
15
  import { NodeCrypto } from "@effect/platform-node";
14
16
  import { Cause, Duration, Effect, Layer, Option, PubSub, Result } from "effect";
15
17
 
16
- import type { NodeDurableHost } from "./host.ts";
17
- import { nodeScheduledInputAdmissionLayer } from "./subscriptions.ts";
18
+ import type { NodeDurableHost } from "./NodeDurableHost.ts";
19
+ import { nodeScheduledInputAdmissionLayer } from "./NodeSubscriptions.ts";
18
20
 
19
21
  /** One bounded hint slot for the single supported Node scheduler. Indexed polling repairs loss. */
20
22
  export const nodeScheduleWakeLayer: Layer.Layer<ScheduleWake> = Layer.effect(