@effect-agent/platform-node 0.1.0-beta.41 → 0.1.0-beta.44
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 +15 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -48
- package/src/host.ts +2 -0
- package/src/layers.ts +15 -0
- package/src/scheduling.ts +9 -0
- package/src/subscriptions.ts +7 -0
- package/src/wake-scheduler.ts +3 -0
package/dist/index.d.mts
CHANGED
|
@@ -194,9 +194,22 @@ declare class NodeDurableHost extends NodeDurableHost_base {
|
|
|
194
194
|
* Startup recovery and shutdown gates are unchanged. Workers start only when the caller
|
|
195
195
|
* runs runResolvedWorkers; this constructor never starts a background worker.
|
|
196
196
|
*/
|
|
197
|
-
static layerRegistered<const Entries extends ReadonlyArray<AgentRegistration>, ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never>(registrations: Entries, options: NodeDurableRuntimeOptions<ContextError, ContextRequirements, AuthorizationError, AuthorizationRequirements>): Layer.Layer<NodeDurableHost | NodeDurableRuntimeServices, AuthorizationError | ContextError | DurableWorkerFailure | NodeDurableRuntimeInitializationError, Exclude<Exclude<AuthorizationRequirements, Crypto.Crypto>, Crypto.Crypto> | Exclude<Exclude<ContextRequirements, Crypto.Crypto>, Crypto.Crypto> | Exclude<Exclude<[Entries[number]] extends [never] ? never : Entries[number] extends (infer
|
|
197
|
+
static layerRegistered<const Entries extends ReadonlyArray<AgentRegistration>, ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never>(registrations: Entries, options: NodeDurableRuntimeOptions<ContextError, ContextRequirements, AuthorizationError, AuthorizationRequirements>): Layer.Layer<NodeDurableHost | NodeDurableRuntimeServices, AuthorizationError | ContextError | DurableWorkerFailure | NodeDurableRuntimeInitializationError, Exclude<Exclude<AuthorizationRequirements, Crypto.Crypto>, Crypto.Crypto> | Exclude<Exclude<ContextRequirements, Crypto.Crypto>, Crypto.Crypto> | Exclude<Exclude<[Entries[number]] extends [never] ? never : Entries[number] extends (infer T_2) ? T_2 extends Entries[number] ? T_2 extends {
|
|
198
|
+
readonly attemptLayer: (context: import("@effect-agent/thread").AgentAttemptContext) => Layer.Layer<infer Provides, never, infer Requires>;
|
|
199
|
+
} ? Requires | Exclude<T_2 extends (infer T_3) ? T_3 extends T_2 ? T_3 extends {
|
|
198
200
|
readonly agent: infer A extends import("@effect-agent/thread").ExecutableAgentBinding;
|
|
199
|
-
} ? import("@effect-agent/thread").DurableWorkerRequirements<A> :
|
|
201
|
+
} ? import("@effect-agent/thread").DurableWorkerRequirements<A> : T_3 extends {
|
|
202
|
+
readonly agent: infer D extends import("@effect-agent/core").Definition<Schema.Top, Schema.Top, unknown, import("effect/unstable/ai/Toolkit").Any, import("@effect-agent/core").RunDispositionDeclaration<never, Schema.Top> | undefined, unknown> & {
|
|
203
|
+
readonly instructions: import("@effect-agent/core").InstructionSource<never, unknown, unknown>;
|
|
204
|
+
readonly inputPrompt?: import("@effect-agent/core").InputPromptSource<never, unknown, unknown> | undefined;
|
|
205
|
+
};
|
|
206
|
+
readonly model: infer M extends import("@effect-agent/thread").ExecutableAgentBinding["model"];
|
|
207
|
+
} ? import("@effect-agent/thread").DurableWorkerRequirements<{
|
|
208
|
+
readonly definition: D;
|
|
209
|
+
readonly model: M;
|
|
210
|
+
}> : never : never : never, Provides> : T_2 extends {
|
|
211
|
+
readonly agent: infer A extends import("@effect-agent/thread").ExecutableAgentBinding;
|
|
212
|
+
} ? import("@effect-agent/thread").DurableWorkerRequirements<A> : T_2 extends {
|
|
200
213
|
readonly agent: infer D extends import("@effect-agent/core").Definition<Schema.Top, Schema.Top, unknown, import("effect/unstable/ai/Toolkit").Any, import("@effect-agent/core").RunDispositionDeclaration<never, Schema.Top> | undefined, unknown> & {
|
|
201
214
|
readonly instructions: import("@effect-agent/core").InstructionSource<never, unknown, unknown>;
|
|
202
215
|
readonly inputPrompt?: import("@effect-agent/core").InputPromptSource<never, unknown, unknown> | undefined;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["reportPassFailure"],"sources":["../src/wake-scheduler.ts","../src/layers.ts","../src/host.ts","../src/subscriptions.ts","../src/scheduling.ts"],"sourcesContent":["import type { ThreadId } from \"@effect-agent/core\";\nimport { makeWakeSubscriptionHub, SubmissionLedger, WakeScheduler } from \"@effect-agent/thread\";\nimport type { Duration } from \"effect\";\nimport { Context, Effect, Layer, PubSub, Stream } from \"effect\";\n\n/**\n * Bounded in-process wake buffer. Wake hints are droppable by contract (the ledger-scan fallback\n * keeps liveness), so a full buffer slides out the oldest hint instead of growing without bound.\n */\nconst WAKE_BUFFER_CAPACITY = 1_024;\n\n/** Cadence authority for the Node wake scheduler's ledger-scan fallback loop. */\nexport class NodeWakeSchedulerConfig extends Context.Service<\n NodeWakeSchedulerConfig,\n {\n /** Interval between ledger scans that re-emit every nonterminal Thread lane. */\n readonly scanInterval: Duration.Duration;\n }\n>()(\"@effect-agent/platform-node/NodeWakeSchedulerConfig\") {\n static layer(options: {\n readonly scanInterval: Duration.Duration;\n }): Layer.Layer<NodeWakeSchedulerConfig> {\n return Layer.succeed(NodeWakeSchedulerConfig)({ scanInterval: options.scanInterval });\n }\n}\n\nconst makeWakeScheduler = Effect.gen(function* () {\n const ledger = yield* SubmissionLedger;\n const config = yield* NodeWakeSchedulerConfig;\n const hints = yield* PubSub.sliding<ThreadId>(WAKE_BUFFER_CAPACITY);\n const progress = yield* makeWakeSubscriptionHub;\n yield* Effect.addFinalizer(() => PubSub.shutdown(hints));\n\n /**\n * One fallback scan: every Thread lane with nonterminal work, deduplicated. A scan\n * failure degrades to \"no hints this round\" — the wake channel has no error contract and the\n * next round retries — but is logged so a persistently failing ledger stays visible.\n */\n const scanOnce: Effect.Effect<ReadonlyArray<ThreadId>> = Stream.runCollect(\n ledger.scanNonterminal,\n ).pipe(\n Effect.map((snapshots) => {\n const lanes = new Set<ThreadId>();\n for (const snapshot of snapshots) {\n lanes.add(snapshot.threadId);\n }\n return [...lanes];\n }),\n Effect.catch((error) =>\n Effect.logWarning(\"NodeWakeScheduler fallback scan failed\", error).pipe(\n Effect.as([] as ReadonlyArray<ThreadId>),\n ),\n ),\n );\n\n /**\n * Deployment §3: correctness must not depend on in-memory notifications, so every `wakes` run\n * merges its PubSub subscription with a Clock-driven ledger-scan loop. Both live entirely in\n * the consuming run's Scope; no fiber outlives its subscriber.\n */\n const fallbackScans: Stream.Stream<ThreadId> = Stream.fromIterableEffectRepeat(\n Effect.sleep(config.scanInterval).pipe(Effect.andThen(scanOnce)),\n );\n\n return WakeScheduler.of({\n notify: (threadId) =>\n progress\n .notify(threadId)\n .pipe(Effect.andThen(PubSub.publish(hints, threadId)), Effect.asVoid),\n subscribe: progress.subscribe,\n wakes: Stream.merge(Stream.fromPubSub(hints), fallbackScans),\n });\n});\n\n/**\n * In-process Node `WakeScheduler`: `notify` publishes to a bounded sliding PubSub for prompt\n * same-process wakeups, and every `wakes` subscription additionally runs a periodic\n * `SubmissionLedger.scanNonterminal` fallback so a dropped, coalesced, or never-sent notification\n * can never strand accepted work (persistence §14). Delivery may duplicate; consumers already\n * treat wakes as pure liveness hints.\n */\nexport const nodeWakeSchedulerLayer: Layer.Layer<\n WakeScheduler,\n never,\n SubmissionLedger | NodeWakeSchedulerConfig\n> = Layer.effect(WakeScheduler)(makeWakeScheduler);\n","import type { SubmissionId } from \"@effect-agent/core\";\nimport {\n CurrentToolFailureObserver,\n RunContextPreparationPassthrough,\n RunToolAuthorization,\n toolFailureObserverLayer,\n type RunContextPreparation,\n type RunCostEstimator,\n type RunToolFailureObserver,\n} from \"@effect-agent/engine\";\nimport {\n threadStoreLayer,\n scheduleStoreLayer,\n SqliteStorageConfig,\n SqliteStorageConfigValue,\n storageFailpointLayer,\n submissionLedgerLayer,\n type SqliteStorageFailpointHandler,\n type SqliteStorageInitializationError,\n} from \"@effect-agent/storage-sqlite\";\nimport {\n type ScheduleStore,\n type ThreadStore,\n type WakeScheduler,\n DEFAULT_OWNERSHIP_LEASE_DURATION,\n DeploymentId,\n DurableAgentRuntime,\n DurableRuntimeConfig,\n DurableRuntimeFailpoint,\n ProducerId,\n ReleaseOwnershipRequest,\n SubmissionLedger,\n ToolReconciler,\n type DurableRuntimeFailpointHandler,\n type OwnershipToken,\n} from \"@effect-agent/thread\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { SqliteClient } from \"@effect/sql-sqlite-node\";\nimport { Context, type Crypto, Duration, Effect, Layer, Ref, Schema } from \"effect\";\n\nimport { NodeWakeSchedulerConfig, nodeWakeSchedulerLayer } from \"./wake-scheduler.ts\";\n\nconst PositiveMillis = Schema.Int.check(Schema.isGreaterThan(0));\nconst NonNegativeMillis = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));\nconst WorkerConcurrency = Schema.Int.check(\n Schema.isGreaterThanOrEqualTo(1),\n Schema.isLessThanOrEqualTo(64),\n);\n\n/** The supplied Node durable runtime configuration failed schema validation (DEPLOY-003). */\nexport class NodePlatformConfigError extends Schema.TaggedError<NodePlatformConfigError>()(\n \"NodePlatformConfigError\",\n {\n message: Schema.String,\n cause: Schema.optionalKey(Schema.Defect()),\n },\n) {}\n\n/**\n * Validated Node durable runtime configuration (deployment §4: decoded once during Layer\n * construction, exposed as a typed service). Every cadence is in milliseconds and every bound is\n * finite; `workerConcurrency` caps how many worker loops `NodeDurableHost.runWorkers` drives.\n */\nexport class NodeDurableRuntimeConfigValue extends Schema.Class<NodeDurableRuntimeConfigValue>(\n \"@effect-agent/platform-node/NodeDurableRuntimeConfigValue\",\n)({\n /** SQLite database file backing BOTH the Thread Log and the Submission Ledger. */\n filename: Schema.NonEmptyString,\n deploymentId: DeploymentId,\n producerId: ProducerId,\n /** Submission ownership lease duration (D5); liveness hint only, epochs stay authoritative. */\n ownershipLeaseDuration: PositiveMillis,\n /** Finite bound on concurrent worker loops per host (rule 10). */\n workerConcurrency: WorkerConcurrency,\n /** Ledger-scan fallback cadence of the Node wake scheduler (deployment §3). */\n wakeScanInterval: PositiveMillis,\n /** `awaitSettlement` ledger re-check cadence when no wake arrives. */\n settlementPollInterval: PositiveMillis,\n /** Worker ownership-lease renewal cadence. */\n leaseRenewalInterval: PositiveMillis,\n /** Active-Run abort-intent poll cadence. */\n abortPollInterval: PositiveMillis,\n /** Bounded SQLITE_BUSY retry window for write-lock acquisition. */\n busyTimeout: NonNegativeMillis,\n /** Canonical observation poll cadence of the SQLite store. */\n observationPollInterval: NonNegativeMillis,\n /** Opt-in full payload/digest-chain audit while opening the store. */\n verifyOnOpen: Schema.Boolean,\n}) {}\n\n/** Explicit configuration authority for the assembled Node durable runtime. */\nexport class NodeDurableRuntimeConfig extends Context.Service<\n NodeDurableRuntimeConfig,\n NodeDurableRuntimeConfigValue\n>()(\"@effect-agent/platform-node/NodeDurableRuntimeConfig\") {}\n\n/**\n * Raw (unvalidated) construction options for `NodeDurableRuntime.layer`. Optional fields default\n * to the documented production values; everything is schema-decoded into\n * `NodeDurableRuntimeConfigValue` before any resource opens (deployment §5 gate 1).\n */\nexport interface NodeDurableRuntimeOptions<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n> {\n readonly filename: string;\n readonly deploymentId: string;\n readonly producerId: string;\n /** Milliseconds; default `DEFAULT_OWNERSHIP_LEASE_DURATION` (30s, D5). */\n readonly ownershipLeaseDuration?: number | undefined;\n /** Default 1; bounded to 1..64. */\n readonly workerConcurrency?: number | undefined;\n /** Milliseconds; default 1000. */\n readonly wakeScanInterval?: number | undefined;\n /** Milliseconds; default 500. */\n readonly settlementPollInterval?: number | undefined;\n /** Milliseconds; default 10000. */\n readonly leaseRenewalInterval?: number | undefined;\n /** Milliseconds; default 500. */\n readonly abortPollInterval?: number | undefined;\n /** Deployment-owned pricing authority used by durable cost budgets and settlements. */\n readonly estimateCostMicrousd?: RunCostEstimator | undefined;\n /** Closed trusted Tool failure reporting. Omission masks ambient observers at construction. */\n readonly toolFailureObserver?: RunToolFailureObserver | undefined;\n /** Milliseconds; default 5000. */\n readonly busyTimeout?: number | undefined;\n /** Milliseconds; default 25. */\n readonly observationPollInterval?: number | undefined;\n /** Default false. */\n readonly verifyOnOpen?: boolean | undefined;\n /** SQLite adapter fault injection (`ledger:*` / `append:*` locations); default none. */\n readonly storageFailpoint?: SqliteStorageFailpointHandler | undefined;\n /** Coordinator fault injection (`submit:*` / `terminalize:*` locations); default none. */\n readonly runtimeFailpoint?: DurableRuntimeFailpointHandler | undefined;\n /**\n * Reconciliation policy consulted for open ordinary Tool Calls before an Unknown Outcome is\n * recorded (durability §10, DUR-009). Defaults to the fail-closed `ToolReconciler.uncertain`:\n * with no registered policy, every open call stays Unknown and routes to the authorized\n * DUR-017 resolution path.\n */\n readonly toolReconciler?: Layer.Layer<ToolReconciler> | undefined;\n /** Host prompt preparation/compaction, acquired once with the runtime; default pass-through. */\n readonly runContext?:\n | Layer.Layer<RunContextPreparation, ContextError, ContextRequirements | Crypto.Crypto>\n | undefined;\n /**\n * Independent action-time Tool authority, acquired once with the runtime; default allow-all.\n * Construction errors and application dependencies remain in the assembled Layer's E and R.\n * The platform supplies Crypto to both extension Layers.\n */\n readonly toolAuthorization?:\n | Layer.Layer<\n RunToolAuthorization,\n AuthorizationError,\n AuthorizationRequirements | Crypto.Crypto\n >\n | undefined;\n}\n\n/** Built-in construction failures. `layer` also preserves supplied service Layers' errors. */\nexport type NodeDurableRuntimeInitializationError =\n | NodePlatformConfigError\n | SqliteStorageInitializationError;\n\n/** The services `NodeDurableRuntime.layer` provides. */\nexport type NodeDurableRuntimeServices =\n | DurableAgentRuntime\n | SubmissionLedger\n | ThreadStore\n | ScheduleStore\n | WakeScheduler\n | DurableRuntimeConfig\n | NodeDurableRuntimeConfig;\n\nconst decodeConfigValue = Schema.decodeUnknownEffect(NodeDurableRuntimeConfigValue);\n\nconst configFromOptions = (\n options: Omit<NodeDurableRuntimeOptions, \"runContext\" | \"toolAuthorization\">,\n): Effect.Effect<NodeDurableRuntimeConfigValue, NodePlatformConfigError> =>\n decodeConfigValue({\n filename: options.filename,\n deploymentId: options.deploymentId,\n producerId: options.producerId,\n ownershipLeaseDuration:\n options.ownershipLeaseDuration ?? Duration.toMillis(DEFAULT_OWNERSHIP_LEASE_DURATION),\n workerConcurrency: options.workerConcurrency ?? 1,\n wakeScanInterval: options.wakeScanInterval ?? 1_000,\n settlementPollInterval: options.settlementPollInterval ?? 500,\n leaseRenewalInterval: options.leaseRenewalInterval ?? 10_000,\n abortPollInterval: options.abortPollInterval ?? 500,\n busyTimeout: options.busyTimeout ?? 5_000,\n observationPollInterval: options.observationPollInterval ?? 25,\n verifyOnOpen: options.verifyOnOpen ?? false,\n }).pipe(\n Effect.mapError((error) =>\n NodePlatformConfigError.make({\n message: `Invalid Node durable runtime configuration: ${error.message}`,\n cause: error,\n }),\n ),\n );\n\n/** SQLite storage configuration derived from the single validated Node configuration. */\nconst sqliteStorageConfigLayer: Layer.Layer<SqliteStorageConfig, never, NodeDurableRuntimeConfig> =\n Layer.effect(SqliteStorageConfig)(\n Effect.gen(function* () {\n const config = yield* NodeDurableRuntimeConfig;\n return SqliteStorageConfigValue.make({\n observationPollInterval: config.observationPollInterval,\n busyTimeout: config.busyTimeout,\n ownershipLeaseDuration: config.ownershipLeaseDuration,\n verifyOnOpen: config.verifyOnOpen,\n });\n }),\n );\n\n/** Thread coordinator configuration derived from the single validated Node configuration. */\nconst durableRuntimeConfigLayer = (\n estimateCostMicrousd: RunCostEstimator | undefined,\n): Layer.Layer<DurableRuntimeConfig, never, NodeDurableRuntimeConfig> =>\n Layer.effect(DurableRuntimeConfig)(\n Effect.gen(function* () {\n const config = yield* NodeDurableRuntimeConfig;\n return DurableRuntimeConfig.make({\n deploymentId: config.deploymentId,\n producerId: config.producerId,\n settlementPollInterval: Duration.millis(config.settlementPollInterval),\n leaseRenewalInterval: Duration.millis(config.leaseRenewalInterval),\n abortPollInterval: Duration.millis(config.abortPollInterval),\n ...(estimateCostMicrousd === undefined ? {} : { estimateCostMicrousd }),\n });\n }),\n );\n\n/** Wake fallback-scan cadence derived from the single validated Node configuration. */\nconst wakeSchedulerConfigLayer: Layer.Layer<\n NodeWakeSchedulerConfig,\n never,\n NodeDurableRuntimeConfig\n> = Layer.effect(NodeWakeSchedulerConfig)(\n Effect.gen(function* () {\n const config = yield* NodeDurableRuntimeConfig;\n return { scanInterval: Duration.millis(config.wakeScanInterval) };\n }),\n);\n\nconst releaseTrackedOwnership = (\n ledger: SubmissionLedger[\"Service\"],\n registry: Ref.Ref<ReadonlyMap<SubmissionId, OwnershipToken>>,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n const tracked = yield* Ref.getAndSet(registry, new Map<SubmissionId, OwnershipToken>());\n for (const [submissionId, ownershipToken] of tracked) {\n yield* ledger\n .releaseOwnership(ReleaseOwnershipRequest.make({ submissionId, ownershipToken }))\n .pipe(\n Effect.catchTags({\n // A newer epoch already owns (or settled) the lane: nothing left to drain.\n OwnershipLost: () => Effect.void,\n // Drain is best-effort by design: the lease still expires and the durability protocol,\n // not graceful shutdown, provides correctness (DEPLOY-006).\n LedgerError: (error) =>\n Effect.logWarning(\"Ownership drain failed; the lease will expire instead\", error),\n }),\n );\n }\n });\n\n/**\n * Shutdown-drain decorator for a `SubmissionLedger` (deployment §6 step 6): every ownership\n * period granted through this Layer is tracked — claims start tracking, renewals follow token\n * rotation, releases and settlement finalizations stop it — and every ownership still held when\n * the Layer's Scope closes is released so another host can claim the lane immediately instead of\n * waiting for lease expiry. The drain is a liveness courtesy only; producer-epoch fencing remains\n * the correctness authority (DUR-006), and a forced kill simply falls back to lease expiry.\n */\nexport const ownershipDrainLayer: Layer.Layer<SubmissionLedger, never, SubmissionLedger> =\n Layer.effect(SubmissionLedger)(\n Effect.gen(function* () {\n const ledger = yield* SubmissionLedger;\n const registry = yield* Ref.make<ReadonlyMap<SubmissionId, OwnershipToken>>(\n new Map<SubmissionId, OwnershipToken>(),\n );\n\n const track = (submissionId: SubmissionId, ownershipToken: OwnershipToken) =>\n Ref.update(registry, (tracked) => new Map(tracked).set(submissionId, ownershipToken));\n const untrack = (submissionId: SubmissionId) =>\n Ref.update(registry, (tracked) => {\n const next = new Map(tracked);\n next.delete(submissionId);\n return next;\n });\n\n yield* Effect.addFinalizer(() => releaseTrackedOwnership(ledger, registry));\n\n return SubmissionLedger.of({\n capabilities: ledger.capabilities,\n admit: ledger.admit,\n markReady: ledger.markReady,\n lookup: ledger.lookup,\n // The S2 subagent ops forward untouched: none of them grants an ownership period, so\n // the drain has nothing to track for them (`suspend` below already stops tracking the\n // waitingForChild ownership period the moment it ends).\n resolveAdmission: ledger.resolveAdmission,\n recordChildSettled: ledger.recordChildSettled,\n reserveChildBudget: ledger.reserveChildBudget,\n attachChildToReservation: ledger.attachChildToReservation,\n beginChildBudgetRelease: ledger.beginChildBudgetRelease,\n releaseChildBudget: ledger.releaseChildBudget,\n claim: (request) =>\n ledger\n .claim(request)\n .pipe(\n Effect.tap((claimed) =>\n claimed._tag === \"Some\"\n ? track(claimed.value.submissionId, claimed.value.ownershipToken)\n : Effect.void,\n ),\n ),\n renewOwnership: (request) =>\n ledger.renewOwnership(request).pipe(\n Effect.tap((renewal) => track(request.submissionId, renewal.ownershipToken)),\n Effect.tapError((error) =>\n error._tag === \"OwnershipLost\" ? untrack(request.submissionId) : Effect.void,\n ),\n ),\n releaseOwnership: (request) =>\n ledger.releaseOwnership(request).pipe(\n Effect.tap(() => untrack(request.submissionId)),\n Effect.tapError((error) =>\n error._tag === \"OwnershipLost\" ? untrack(request.submissionId) : Effect.void,\n ),\n ),\n markInputApplied: ledger.markInputApplied,\n reserveSettlement: ledger.reserveSettlement,\n finalizeSettlement: (request) =>\n ledger.finalizeSettlement(request).pipe(Effect.tap(() => untrack(request.submissionId))),\n requestAbort: ledger.requestAbort,\n claimJoining: ledger.claimJoining,\n markJoined: ledger.markJoined,\n revertJoining: ledger.revertJoining,\n // Suspension ends the ownership period by contract, so the drain stops tracking it.\n suspend: (request) =>\n ledger.suspend(request).pipe(Effect.tap(() => untrack(request.submissionId))),\n recordApprovalDecision: ledger.recordApprovalDecision,\n markUnknown: ledger.markUnknown,\n recordUnknownResolution: ledger.recordUnknownResolution,\n scanNonterminal: ledger.scanNonterminal,\n loadRecoverySnapshot: ledger.loadRecoverySnapshot,\n });\n }),\n );\n\n/**\n * The DN Layer assembly (deployment §12: a Layer-assembly library, not an app entrypoint).\n * `layer(options)` decodes the configuration, opens ONE SQLite database serving both the\n * Thread Log and the Submission Ledger (so claims fence the same producer epochs), wires\n * the Node wake scheduler with its ledger-scan fallback, wraps the ledger with the shutdown\n * ownership drain, defaults the Tool reconciliation policy to the fail-closed\n * `ToolReconciler.uncertain` (override via `options.toolReconciler`), and provides a ready\n * `DurableAgentRuntime` on top. Storage compatibility is\n * verified during construction: an incompatible database file fails the Layer with\n * `SqliteStorageCompatibilityError` before anything is mutated (DEPLOY-008).\n */\nexport class NodeDurableRuntime {\n /** Validated configuration Layer; fails typed when the supplied options are out of bounds. */\n static configLayer<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n >(\n options: NodeDurableRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements\n >,\n ): Layer.Layer<NodeDurableRuntimeConfig, NodePlatformConfigError> {\n return Layer.effect(NodeDurableRuntimeConfig)(configFromOptions(options));\n }\n\n /** The full DN runtime stack over one SQLite file. */\n static layer<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n >(\n options: NodeDurableRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements\n >,\n ): Layer.Layer<\n NodeDurableRuntimeServices,\n NodeDurableRuntimeInitializationError | ContextError | AuthorizationError,\n Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>\n > {\n return Layer.unwrap(\n Effect.map(configFromOptions(options), (config) => {\n const nodeConfigLayer = Layer.succeed(NodeDurableRuntimeConfig)(config);\n const infrastructure = Layer.mergeAll(\n sqliteStorageConfigLayer,\n storageFailpointLayer({\n filename: config.filename,\n failpoint: options.storageFailpoint,\n }),\n SqliteClient.layer({ filename: config.filename }),\n NodeCrypto.layer,\n );\n const runtimeFailpointLayer =\n options.runtimeFailpoint === undefined\n ? DurableRuntimeFailpoint.layer\n : Layer.succeed(DurableRuntimeFailpoint)({ hit: options.runtimeFailpoint });\n const reconcilerLayer = options.toolReconciler ?? ToolReconciler.uncertain;\n const observerLayer =\n options.toolFailureObserver === undefined\n ? Layer.succeed(CurrentToolFailureObserver)(undefined)\n : toolFailureObserverLayer(options.toolFailureObserver);\n const ports = Layer.mergeAll(\n threadStoreLayer,\n scheduleStoreLayer,\n nodeWakeSchedulerLayer.pipe(\n Layer.provideMerge(ownershipDrainLayer.pipe(Layer.provide(submissionLedgerLayer))),\n ),\n );\n return DurableAgentRuntime.layerWithServices.pipe(\n Layer.provideMerge(\n Layer.mergeAll(ports, durableRuntimeConfigLayer(options.estimateCostMicrousd)),\n ),\n Layer.provide(\n Layer.mergeAll(\n wakeSchedulerConfigLayer,\n runtimeFailpointLayer,\n reconcilerLayer,\n observerLayer,\n ),\n ),\n Layer.provideMerge(infrastructure),\n Layer.provideMerge(nodeConfigLayer),\n Layer.provide(\n Layer.mergeAll(\n options.runContext ?? RunContextPreparationPassthrough,\n options.toolAuthorization ?? RunToolAuthorization.allowAll,\n ).pipe(Layer.provide(NodeCrypto.layer)),\n ),\n );\n }),\n );\n }\n}\n","import type { ThreadId, SubmissionId } from \"@effect-agent/core\";\nimport {\n compileRegistrations,\n type AgentRegistration,\n DurableAgentRuntime,\n type AbortCommand,\n type AbortIntent,\n type CanonicalRecordEnvelope,\n type ThreadNotMaterialized,\n type ThreadStoreError,\n type DurableAbortFailure,\n type DurableAwaitFailure,\n type ResolvedBinding,\n type DurableBindingFailure,\n type DurableExplainFailure,\n type DurableObserveOptions,\n type DurableObligationFailure,\n type DurableRetryFailure,\n type DurableSubmitAgent,\n type DurableSubmitFailure,\n type DurableSubmitOptions,\n type DurableVerifyFailure,\n type DurableWorkerFailure,\n type IntegrityReport,\n type ObligationReport,\n type ObligationThresholds,\n type OperationDenied,\n type Receipt,\n type RecoveryExplanation,\n type RecoveryReport,\n type RetryCommand,\n type Settlement,\n} from \"@effect-agent/thread\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { type Stream, Context, type Crypto, Effect, Layer, Ref, Schema } from \"effect\";\n\nimport {\n NodeDurableRuntime,\n NodeDurableRuntimeConfig,\n type NodeDurableRuntimeInitializationError,\n type NodeDurableRuntimeOptions,\n type NodeDurableRuntimeServices,\n} from \"./layers.ts\";\n\n/**\n * Admission is not open on this host: it is shutting down (deployment §6 step 1, DEPLOY-005).\n * Accepted work is unaffected — only NEW admissions are refused.\n */\nexport class AdmissionClosed extends Schema.TaggedError<AdmissionClosed>()(\"AdmissionClosed\", {\n message: Schema.String,\n}) {}\n\nconst makeHost = (bindings: ReadonlyArray<ResolvedBinding>) =>\n Effect.gen(function* () {\n const runtime = yield* DurableAgentRuntime;\n const config = yield* NodeDurableRuntimeConfig;\n\n // Startup gate (deployment §5, plan §host): configuration decoding and storage compatibility\n // already gated this Layer's dependencies; the last gate before admission opens is recovering\n // EVERY nonterminal Submission. Work needing a live Agent Binding is reported `deferred` and\n // stays a visible obligation for `runWorkers`; lanes durably blocked on an Unknown Outcome are\n // reported `unknown` and wait for the authorized `resolveUnknown` path (DUR-017) — they consume\n // no worker permit while the settlement obligation stays owed.\n const startupRecovery = yield* runtime.runRecovery;\n\n const admission = yield* Ref.make(true);\n // Shutdown step 1 (DEPLOY-005): admission closes before the ownership drain in the runtime\n // Layer below releases claims and before the stores close in reverse acquisition order.\n yield* Effect.addFinalizer(() => Ref.set(admission, false));\n\n const requireAdmission: Effect.Effect<void, AdmissionClosed> = Ref.get(admission).pipe(\n Effect.flatMap((open) =>\n open\n ? Effect.void\n : Effect.fail(\n AdmissionClosed.make({ message: \"The host is shutting down; admission is closed.\" }),\n ),\n ),\n );\n\n const submit = <InputSchema extends Schema.Top>(\n agent: DurableSubmitAgent<InputSchema>,\n input: InputSchema[\"Type\"],\n options: DurableSubmitOptions,\n ): Effect.Effect<\n Receipt,\n AdmissionClosed | DurableSubmitFailure,\n InputSchema[\"EncodingServices\"]\n > => requireAdmission.pipe(Effect.andThen(runtime.submit(agent, input, options)));\n\n const runWorkers = <A, E, R>(worker: Effect.Effect<A, E, R>): Effect.Effect<void, E, R> =>\n Effect.forEach(\n Array.from({ length: config.workerConcurrency }, (_, index) => index),\n () => worker,\n { concurrency: \"unbounded\", discard: true },\n );\n\n // S2 multi-binding pool: every claimed head resolves its exact registered Binding through\n // the host's exact registrations, so one bounded\n // pool serves parent and child lanes — the spec §12 smallest-pool suspension/wakeup proof\n // runs `workerConcurrency: 1` over exactly this loop.\n const runResolvedWorkers = runWorkers(runtime.runResolvedWorker(bindings));\n\n return NodeDurableHost.of({\n startupRecovery,\n admissionOpen: Ref.get(admission),\n submit,\n awaitSettlement: runtime.awaitSettlement,\n observe: runtime.observe,\n abort: runtime.abort,\n explain: runtime.explain,\n explainThread: runtime.explainThread,\n verify: runtime.verify,\n retry: runtime.retry,\n wake: runtime.wake,\n scanObligations: runtime.scanObligations,\n runWorkers,\n runResolvedWorkers,\n });\n });\n\n/**\n * Operational host lifecycle for the DN runtime (deployment §2/§5/§6).\n *\n * Startup gates run during Layer construction, so the service existing implies readiness:\n * configuration was schema-decoded, the SQLite file passed the exact-version compatibility check,\n * and every nonterminal Submission went through one full recovery pass BEFORE admission opened.\n * `startupRecovery` is the auditable evidence of that reconciliation pass.\n *\n * Shutdown runs in reverse Layer order when the owning Scope closes: `submit` starts refusing\n * with `AdmissionClosed` first, then the runtime Layer's ownership drain releases every claim\n * still held so another host can take over the lanes immediately, then the SQLite resources\n * close. Forced termination at any point stays safe — the durability protocol, not graceful\n * shutdown, provides correctness (DEPLOY-006).\n */\nexport class NodeDurableHost extends Context.Service<\n NodeDurableHost,\n {\n /**\n * The recovery decisions executed (or deferred) by this host's startup reconciliation.\n * Reports with the `unknown` disposition identify lanes blocked on Unknown Outcomes that\n * only the authorized DUR-017 resolution path can release.\n */\n readonly startupRecovery: ReadonlyArray<RecoveryReport>;\n /** Admission-role readiness (deployment §7): true until shutdown begins. */\n readonly admissionOpen: Effect.Effect<boolean>;\n /** `DurableAgentRuntime.submit` behind the host admission gate. */\n readonly submit: <InputSchema extends Schema.Top>(\n agent: DurableSubmitAgent<InputSchema>,\n input: InputSchema[\"Type\"],\n options: DurableSubmitOptions,\n ) => Effect.Effect<\n Receipt,\n AdmissionClosed | DurableSubmitFailure,\n InputSchema[\"EncodingServices\"]\n >;\n readonly awaitSettlement: (receipt: Receipt) => Effect.Effect<Settlement, DurableAwaitFailure>;\n readonly observe: (\n receipt: Receipt,\n options?: DurableObserveOptions,\n ) => Stream.Stream<\n CanonicalRecordEnvelope,\n ThreadStoreError | ThreadNotMaterialized | OperationDenied\n >;\n readonly abort: (command: AbortCommand) => Effect.Effect<AbortIntent, DurableAbortFailure>;\n /** `DurableAgentRuntime.explain` — read-only recovery explanation of one Submission (P7). */\n readonly explain: (\n submissionId: SubmissionId,\n ) => Effect.Effect<RecoveryExplanation, DurableExplainFailure>;\n /** `DurableAgentRuntime.explainThread` — explain every nonterminal lane member. */\n readonly explainThread: (\n threadId: ThreadId,\n ) => Effect.Effect<ReadonlyArray<RecoveryExplanation>, DurableExplainFailure>;\n /** `DurableAgentRuntime.verify` — read-only integrity checks, never a repair (P7). */\n readonly verify: (threadId: ThreadId) => Effect.Effect<IntegrityReport, DurableVerifyFailure>;\n /** `DurableAgentRuntime.retry` — audited single-Submission re-drive with typed refusals. */\n readonly retry: (command: RetryCommand) => Effect.Effect<RecoveryReport, DurableRetryFailure>;\n /** `DurableAgentRuntime.wake` — the documented operator liveness nudge for one lane. */\n readonly wake: (threadId: ThreadId) => Effect.Effect<void, OperationDenied>;\n /** `DurableAgentRuntime.scanObligations` — the scan-based DUR-017/OPS-001 report. */\n readonly scanObligations: (\n thresholds: ObligationThresholds,\n ) => Effect.Effect<ObligationReport, DurableObligationFailure>;\n /**\n * Run `workerConcurrency` copies of the given worker effect (typically\n * `DurableAgentRuntime.runWorker(agent)`) until the caller's Scope interrupts them. The\n * bound is the validated finite configuration value; the host never forks daemon fibers.\n */\n readonly runWorkers: <A, E, R>(worker: Effect.Effect<A, E, R>) => Effect.Effect<void, E, R>;\n /**\n * Run `workerConcurrency` copies of `DurableAgentRuntime.runResolvedWorker` over the host's\n * registered Bindings (S2): every claimed head resolves its exact stored Binding before any\n * code runs (SUB-023), so one bounded pool serves parent and attached-child lanes.\n */\n readonly runResolvedWorkers: Effect.Effect<void, DurableWorkerFailure | DurableBindingFailure>;\n }\n>()(\"@effect-agent/platform-node/NodeDurableHost\") {\n /**\n * Compile typed registrations and acquire the complete host in one Layer Scope.\n * Node supplies Crypto; model, tool, instruction, and schema services remain required.\n * Startup recovery and shutdown gates are unchanged. Workers start only when the caller\n * runs runResolvedWorkers; this constructor never starts a background worker.\n */\n static layerRegistered<\n const Entries extends ReadonlyArray<AgentRegistration>,\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n >(\n registrations: Entries,\n options: NodeDurableRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements\n >,\n ) {\n return Layer.unwrap(\n Effect.map(compileRegistrations(registrations), (bindings) =>\n NodeDurableHost.layerStack({ ...options, bindings }),\n ),\n ).pipe(Layer.provide(NodeCrypto.layer));\n }\n\n /**\n * Host gates over an assembled `NodeDurableRuntime` stack. Bindings must carry the exact\n * digests stored by submitters. Omission registers no Agents, so resolved work fails closed.\n */\n static readonly layer = (\n bindings: ReadonlyArray<ResolvedBinding> = [],\n ): Layer.Layer<\n NodeDurableHost,\n DurableWorkerFailure,\n DurableAgentRuntime | NodeDurableRuntimeConfig\n > => Layer.effect(NodeDurableHost)(makeHost(bindings));\n\n /** The complete DN host: `NodeDurableRuntime.layer(options)` plus the host lifecycle gates. */\n static layerStack<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n >(\n options: NodeDurableRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements\n > & { readonly bindings?: ReadonlyArray<ResolvedBinding> },\n ): Layer.Layer<\n NodeDurableHost | NodeDurableRuntimeServices,\n | DurableWorkerFailure\n | NodeDurableRuntimeInitializationError\n | ContextError\n | AuthorizationError,\n Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>\n > {\n const { bindings = [], ...runtimeOptions } = options;\n return NodeDurableHost.layer(bindings).pipe(\n Layer.provideMerge(NodeDurableRuntime.layer(runtimeOptions)),\n );\n }\n}\n","import type { AgentId } from \"@effect-agent/core\";\nimport {\n type DurableSubmitAgent,\n type EventSources,\n type SubscriptionInputBindings,\n PersistedJson,\n type PreparedInput,\n PreparedInputAdmission,\n type ScheduledEnvelope,\n ScheduledInputAdmission,\n ScheduledInputRetryable,\n type SubscriptionAuthorizer,\n SubscriptionDriver,\n type SubscriptionError,\n SubscriptionIntake,\n type SubscriptionLimits,\n type SubscriptionStoreFailure,\n SubscriptionStore,\n Subscriptions,\n defaultSubscriptionLimits,\n ScheduleStorageError,\n} from \"@effect-agent/thread\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { Cause, Duration, Effect, Exit, Layer, Option } from \"effect\";\n\nimport { NodeDurableHost } from \"./host.ts\";\n\nconst passthroughSubmitAgent = (agentId: AgentId): DurableSubmitAgent<typeof PersistedJson> => ({\n definition: { id: agentId, input: PersistedJson },\n});\n\nconst ambiguous = (): ScheduledInputRetryable =>\n ScheduledInputRetryable.make({ reason: \"ambiguous\" });\n\nconst corrupt = (operation: string): ScheduleStorageError =>\n ScheduleStorageError.make({ operation, reason: \"corrupt\" });\n\n/** Ordinary prepared admission through the Scope-owned Node host gate. */\nexport const nodePreparedInputAdmissionLayer: Layer.Layer<\n PreparedInputAdmission,\n never,\n NodeDurableHost\n> = Layer.effect(\n PreparedInputAdmission,\n Effect.gen(function* () {\n const host = yield* NodeDurableHost;\n return PreparedInputAdmission.of({\n submit: (envelope) =>\n host\n .submit(passthroughSubmitAgent(envelope.agentId), envelope.input, {\n threadId: envelope.threadId,\n principal: envelope.deliveryPrincipal,\n idempotencyKey: envelope.admissionKey,\n definitions: envelope.definitions,\n })\n .pipe(\n Effect.catchTags({\n AdmissionClosed: () =>\n Effect.fail(ScheduledInputRetryable.make({ reason: \"host-closed\" })),\n AgentInputError: () => Effect.fail(corrupt(\"prepared admission input\")),\n AdmissionConflict: () => Effect.fail(corrupt(\"prepared admission conflict\")),\n DigestError: () => Effect.fail(ambiguous()),\n LedgerError: () => Effect.fail(ambiguous()),\n ThreadStoreError: () => Effect.fail(ambiguous()),\n ThreadNotMaterialized: () => Effect.fail(ambiguous()),\n AppendConflict: () => Effect.fail(ambiguous()),\n FenceRejected: () => Effect.fail(ambiguous()),\n DurableRuntimeFailpointError: () => Effect.fail(ambiguous()),\n }),\n ),\n });\n }),\n);\n\nconst preparedFromSchedule = (envelope: ScheduledEnvelope): PreparedInput => ({\n schemaVersion: 1,\n threadId: envelope.threadId,\n deliveryPrincipal: envelope.deliveryPrincipal,\n agentId: envelope.agentId,\n definitions: envelope.definitions,\n input: envelope.input,\n inputDigest: envelope.inputDigest,\n admissionKey: envelope.admissionKey,\n authorization: envelope.authorization,\n});\n\n/** Compatibility adapter retaining the public scheduling admission port. */\nconst nodeScheduledInputAdmissionFromPreparedLayer: Layer.Layer<\n ScheduledInputAdmission,\n never,\n PreparedInputAdmission\n> = Layer.effect(\n ScheduledInputAdmission,\n Effect.map(PreparedInputAdmission, (admission) =>\n ScheduledInputAdmission.of({\n submit: (envelope) => admission.submit(preparedFromSchedule(envelope)),\n }),\n ),\n);\n\nexport const nodeScheduledInputAdmissionLayer: Layer.Layer<\n ScheduledInputAdmission,\n never,\n NodeDurableHost\n> = nodeScheduledInputAdmissionFromPreparedLayer.pipe(\n Layer.provide(nodePreparedInputAdmissionLayer),\n);\n\nconst reportPassFailure = (cause: Cause.Cause<SubscriptionStoreFailure>): Effect.Effect<boolean> =>\n Cause.hasInterruptsOnly(cause)\n ? Effect.interrupt\n : Effect.logWarning(\"Node subscription pass failed\").pipe(\n Effect.annotateLogs({\n failureTag: Option.match(Cause.findErrorOption(cause), {\n onNone: () => \"Defect\",\n onSome: (error) => error._tag,\n }),\n }),\n Effect.as(false),\n );\n\nconst nodeSubscriptionDriverLayer = (\n limits: SubscriptionLimits,\n): Layer.Layer<never, never, SubscriptionDriver | SubscriptionStore> =>\n Layer.effectDiscard(\n Effect.gen(function* () {\n const driver = yield* SubscriptionDriver;\n const store = yield* SubscriptionStore;\n\n const run = Effect.gen(function* () {\n while (true) {\n const passSucceeded = yield* driver.runDue.pipe(\n Effect.map((pass) => pass.failed === 0),\n Effect.catchCause(reportPassFailure),\n );\n\n if (!passSucceeded) {\n yield* Effect.sleep(Duration.millis(limits.retryMillis));\n continue;\n }\n\n const deadline = yield* store.nextDeadline.pipe(Effect.exit);\n if (Exit.isFailure(deadline)) {\n yield* reportPassFailure(deadline.cause);\n yield* Effect.sleep(Duration.millis(limits.retryMillis));\n continue;\n }\n\n const nowMillis = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n const delay =\n deadline.value === null\n ? limits.retryMillis\n : Math.max(1, Math.min(deadline.value - nowMillis, limits.retryMillis));\n yield* Effect.sleep(Duration.millis(delay));\n }\n });\n\n yield* Effect.forkScoped(run);\n }),\n );\n\nexport interface NodeSubscriptionsOptions {\n readonly limits?: SubscriptionLimits | undefined;\n}\n\n/**\n * One Scope-owned subscription partition in the sole process owning its SQLite database.\n * Indexed polling repairs restart and lost wake state; closing the Scope interrupts the driver.\n */\nexport class NodeSubscriptions {\n static layer(\n options: NodeSubscriptionsOptions = {},\n ): Layer.Layer<\n Subscriptions | SubscriptionIntake,\n SubscriptionError,\n | NodeDurableHost\n | SubscriptionStore\n | SubscriptionAuthorizer\n | EventSources\n | SubscriptionInputBindings\n > {\n const limits = options.limits ?? defaultSubscriptionLimits;\n const publicServices = Layer.merge(\n Subscriptions.layer(limits),\n SubscriptionIntake.layer(limits),\n );\n const driver = nodeSubscriptionDriverLayer(limits).pipe(\n Layer.provide(SubscriptionDriver.layer(limits)),\n );\n return Layer.merge(publicServices, driver).pipe(\n Layer.provide(nodePreparedInputAdmissionLayer),\n Layer.provide(NodeCrypto.layer),\n );\n }\n}\n","import {\n type ScheduleProcessFailure,\n type ScheduleAuthorizer,\n type SchedulingLimits,\n ScheduleStorageError,\n ScheduleStore,\n type ScheduleValidationError,\n ScheduleWake,\n Scheduling,\n ScheduleDriver,\n defaultSchedulingLimits,\n} from \"@effect-agent/thread\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { Cause, Duration, Effect, Layer, Option, PubSub, Result } from \"effect\";\n\nimport type { NodeDurableHost } from \"./host.ts\";\nimport { nodeScheduledInputAdmissionLayer } from \"./subscriptions.ts\";\n\n/** One bounded hint slot for the single supported Node scheduler. Indexed polling repairs loss. */\nexport const nodeScheduleWakeLayer: Layer.Layer<ScheduleWake> = Layer.effect(\n ScheduleWake,\n Effect.gen(function* () {\n const hints = yield* PubSub.sliding<void>(1);\n const subscription = yield* PubSub.subscribe(hints);\n yield* Effect.addFinalizer(() => PubSub.shutdown(hints));\n return ScheduleWake.of({\n notify: PubSub.publish(hints, undefined).pipe(Effect.asVoid),\n await: PubSub.take(subscription),\n });\n }),\n);\n\nconst reportPassFailure = (cause: Cause.Cause<ScheduleProcessFailure>): Effect.Effect<boolean> =>\n Cause.hasInterruptsOnly(cause)\n ? Effect.interrupt\n : Effect.logWarning(\"Node scheduling pass failed\").pipe(\n Effect.annotateLogs({\n failureTag: Option.match(Cause.findErrorOption(cause), {\n onNone: () => \"Defect\",\n onSome: (error) => error._tag,\n }),\n }),\n Effect.as(false),\n );\n\nconst nodeSchedulingDriverLayer = (\n limits: SchedulingLimits,\n): Layer.Layer<never, never, ScheduleDriver | ScheduleStore | ScheduleWake> =>\n Layer.effectDiscard(\n Effect.gen(function* () {\n const scheduling = yield* ScheduleDriver;\n const store = yield* ScheduleStore;\n const wake = yield* ScheduleWake;\n\n const run = Effect.gen(function* () {\n while (true) {\n const passSucceeded = yield* scheduling.runDue().pipe(\n Effect.map((pass) => pass.failed === 0),\n Effect.catchCause(reportPassFailure),\n );\n const deadlineResult = passSucceeded\n ? yield* store.nextDeadline().pipe(Effect.result)\n : Result.fail(\n ScheduleStorageError.make({ operation: \"driver pass\", reason: \"unavailable\" }),\n );\n if (Result.isFailure(deadlineResult) && passSucceeded) {\n yield* Effect.logWarning(\"Node scheduling deadline query failed\");\n }\n const nowMillis = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n const deadlineDelay =\n Result.isSuccess(deadlineResult) && deadlineResult.success !== null\n ? Math.max(0, deadlineResult.success - nowMillis)\n : limits.recoveryPollMillis;\n const delay = Math.min(deadlineDelay, limits.recoveryPollMillis);\n yield* Effect.raceFirst(wake.await, Effect.sleep(Duration.millis(delay)));\n }\n });\n\n yield* Effect.forkScoped(run);\n }),\n );\n\nexport interface NodeSchedulingOptions {\n readonly limits?: SchedulingLimits | undefined;\n}\n\n/**\n * Optional Scope-owned Node scheduling service and driver. The caller must provide the existing\n * host, its SQLite ScheduleStore, and an explicit ScheduleAuthorizer.\n */\nexport class NodeScheduling {\n static layer(\n options: NodeSchedulingOptions = {},\n ): Layer.Layer<\n Scheduling,\n ScheduleValidationError,\n NodeDurableHost | ScheduleStore | ScheduleAuthorizer\n > {\n const limits = options.limits ?? defaultSchedulingLimits;\n const schedulingWithDriver = nodeSchedulingDriverLayer(limits).pipe(\n Layer.provide(ScheduleDriver.layer(limits)),\n Layer.merge(Scheduling.layer(limits)),\n );\n return schedulingWithDriver.pipe(\n Layer.provide(\n Layer.mergeAll(nodeScheduledInputAdmissionLayer, nodeScheduleWakeLayer, NodeCrypto.layer),\n ),\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["reportPassFailure"],"sources":["../src/wake-scheduler.ts","../src/layers.ts","../src/host.ts","../src/subscriptions.ts","../src/scheduling.ts"],"sourcesContent":["import type { ThreadId } from \"@effect-agent/core\";\nimport { makeWakeSubscriptionHub, SubmissionLedger, WakeScheduler } from \"@effect-agent/thread\";\nimport type { Duration } from \"effect\";\nimport { Context, Effect, Layer, PubSub, Stream } from \"effect\";\n\n/**\n * Bounded in-process wake buffer. Wake hints are droppable by contract (the ledger-scan fallback\n * keeps liveness), so a full buffer slides out the oldest hint instead of growing without bound.\n */\nconst WAKE_BUFFER_CAPACITY = 1_024;\n\n/** Cadence authority for the Node wake scheduler's ledger-scan fallback loop. */\nexport class NodeWakeSchedulerConfig extends Context.Service<\n NodeWakeSchedulerConfig,\n {\n /** Interval between ledger scans that re-emit every nonterminal Thread lane. */\n readonly scanInterval: Duration.Duration;\n }\n>()(\"@effect-agent/platform-node/NodeWakeSchedulerConfig\") {\n static layer(options: {\n readonly scanInterval: Duration.Duration;\n }): Layer.Layer<NodeWakeSchedulerConfig> {\n return Layer.succeed(NodeWakeSchedulerConfig)({ scanInterval: options.scanInterval });\n }\n}\n\nconst makeWakeScheduler = Effect.gen(function* () {\n const ledger = yield* SubmissionLedger;\n const config = yield* NodeWakeSchedulerConfig;\n const hints = yield* PubSub.sliding<ThreadId>(WAKE_BUFFER_CAPACITY);\n const progress = yield* makeWakeSubscriptionHub;\n\n yield* Effect.addFinalizer(() => PubSub.shutdown(hints));\n\n /**\n * One fallback scan: every Thread lane with nonterminal work, deduplicated. A scan\n * failure degrades to \"no hints this round\" — the wake channel has no error contract and the\n * next round retries — but is logged so a persistently failing ledger stays visible.\n */\n const scanOnce: Effect.Effect<ReadonlyArray<ThreadId>> = Stream.runCollect(\n ledger.scanNonterminal,\n ).pipe(\n Effect.map((snapshots) => {\n const lanes = new Set<ThreadId>();\n\n for (const snapshot of snapshots) {\n lanes.add(snapshot.threadId);\n }\n\n return [...lanes];\n }),\n Effect.catch((error) =>\n Effect.logWarning(\"NodeWakeScheduler fallback scan failed\", error).pipe(\n Effect.as([] as ReadonlyArray<ThreadId>),\n ),\n ),\n );\n\n /**\n * Deployment §3: correctness must not depend on in-memory notifications, so every `wakes` run\n * merges its PubSub subscription with a Clock-driven ledger-scan loop. Both live entirely in\n * the consuming run's Scope; no fiber outlives its subscriber.\n */\n const fallbackScans: Stream.Stream<ThreadId> = Stream.fromIterableEffectRepeat(\n Effect.sleep(config.scanInterval).pipe(Effect.andThen(scanOnce)),\n );\n\n return WakeScheduler.of({\n notify: (threadId) =>\n progress\n .notify(threadId)\n .pipe(Effect.andThen(PubSub.publish(hints, threadId)), Effect.asVoid),\n subscribe: progress.subscribe,\n wakes: Stream.merge(Stream.fromPubSub(hints), fallbackScans),\n });\n});\n\n/**\n * In-process Node `WakeScheduler`: `notify` publishes to a bounded sliding PubSub for prompt\n * same-process wakeups, and every `wakes` subscription additionally runs a periodic\n * `SubmissionLedger.scanNonterminal` fallback so a dropped, coalesced, or never-sent notification\n * can never strand accepted work (persistence §14). Delivery may duplicate; consumers already\n * treat wakes as pure liveness hints.\n */\nexport const nodeWakeSchedulerLayer: Layer.Layer<\n WakeScheduler,\n never,\n SubmissionLedger | NodeWakeSchedulerConfig\n> = Layer.effect(WakeScheduler)(makeWakeScheduler);\n","import type { SubmissionId } from \"@effect-agent/core\";\nimport {\n CurrentToolFailureObserver,\n RunContextPreparationPassthrough,\n RunToolAuthorization,\n toolFailureObserverLayer,\n type RunContextPreparation,\n type RunCostEstimator,\n type RunToolFailureObserver,\n} from \"@effect-agent/engine\";\nimport {\n threadStoreLayer,\n scheduleStoreLayer,\n SqliteStorageConfig,\n SqliteStorageConfigValue,\n storageFailpointLayer,\n submissionLedgerLayer,\n type SqliteStorageFailpointHandler,\n type SqliteStorageInitializationError,\n} from \"@effect-agent/storage-sqlite\";\nimport {\n type ScheduleStore,\n type ThreadStore,\n type WakeScheduler,\n DEFAULT_OWNERSHIP_LEASE_DURATION,\n DeploymentId,\n DurableAgentRuntime,\n DurableRuntimeConfig,\n DurableRuntimeFailpoint,\n ProducerId,\n ReleaseOwnershipRequest,\n SubmissionLedger,\n ToolReconciler,\n type DurableRuntimeFailpointHandler,\n type OwnershipToken,\n} from \"@effect-agent/thread\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { SqliteClient } from \"@effect/sql-sqlite-node\";\nimport { Context, type Crypto, Duration, Effect, Layer, Ref, Schema } from \"effect\";\n\nimport { NodeWakeSchedulerConfig, nodeWakeSchedulerLayer } from \"./wake-scheduler.ts\";\n\nconst PositiveMillis = Schema.Int.check(Schema.isGreaterThan(0));\nconst NonNegativeMillis = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));\n\nconst WorkerConcurrency = Schema.Int.check(\n Schema.isGreaterThanOrEqualTo(1),\n Schema.isLessThanOrEqualTo(64),\n);\n\n/** The supplied Node durable runtime configuration failed schema validation (DEPLOY-003). */\nexport class NodePlatformConfigError extends Schema.TaggedError<NodePlatformConfigError>()(\n \"NodePlatformConfigError\",\n {\n message: Schema.String,\n cause: Schema.optionalKey(Schema.Defect()),\n },\n) {}\n\n/**\n * Validated Node durable runtime configuration (deployment §4: decoded once during Layer\n * construction, exposed as a typed service). Every cadence is in milliseconds and every bound is\n * finite; `workerConcurrency` caps how many worker loops `NodeDurableHost.runWorkers` drives.\n */\nexport class NodeDurableRuntimeConfigValue extends Schema.Class<NodeDurableRuntimeConfigValue>(\n \"@effect-agent/platform-node/NodeDurableRuntimeConfigValue\",\n)({\n /** SQLite database file backing BOTH the Thread Log and the Submission Ledger. */\n filename: Schema.NonEmptyString,\n deploymentId: DeploymentId,\n producerId: ProducerId,\n /** Submission ownership lease duration (D5); liveness hint only, epochs stay authoritative. */\n ownershipLeaseDuration: PositiveMillis,\n /** Finite bound on concurrent worker loops per host (rule 10). */\n workerConcurrency: WorkerConcurrency,\n /** Ledger-scan fallback cadence of the Node wake scheduler (deployment §3). */\n wakeScanInterval: PositiveMillis,\n /** `awaitSettlement` ledger re-check cadence when no wake arrives. */\n settlementPollInterval: PositiveMillis,\n /** Worker ownership-lease renewal cadence. */\n leaseRenewalInterval: PositiveMillis,\n /** Active-Run abort-intent poll cadence. */\n abortPollInterval: PositiveMillis,\n /** Bounded SQLITE_BUSY retry window for write-lock acquisition. */\n busyTimeout: NonNegativeMillis,\n /** Canonical observation poll cadence of the SQLite store. */\n observationPollInterval: NonNegativeMillis,\n /** Opt-in full payload/digest-chain audit while opening the store. */\n verifyOnOpen: Schema.Boolean,\n}) {}\n\n/** Explicit configuration authority for the assembled Node durable runtime. */\nexport class NodeDurableRuntimeConfig extends Context.Service<\n NodeDurableRuntimeConfig,\n NodeDurableRuntimeConfigValue\n>()(\"@effect-agent/platform-node/NodeDurableRuntimeConfig\") {}\n\n/**\n * Raw (unvalidated) construction options for `NodeDurableRuntime.layer`. Optional fields default\n * to the documented production values; everything is schema-decoded into\n * `NodeDurableRuntimeConfigValue` before any resource opens (deployment §5 gate 1).\n */\nexport interface NodeDurableRuntimeOptions<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n> {\n readonly filename: string;\n readonly deploymentId: string;\n readonly producerId: string;\n /** Milliseconds; default `DEFAULT_OWNERSHIP_LEASE_DURATION` (30s, D5). */\n readonly ownershipLeaseDuration?: number | undefined;\n /** Default 1; bounded to 1..64. */\n readonly workerConcurrency?: number | undefined;\n /** Milliseconds; default 1000. */\n readonly wakeScanInterval?: number | undefined;\n /** Milliseconds; default 500. */\n readonly settlementPollInterval?: number | undefined;\n /** Milliseconds; default 10000. */\n readonly leaseRenewalInterval?: number | undefined;\n /** Milliseconds; default 500. */\n readonly abortPollInterval?: number | undefined;\n /** Deployment-owned pricing authority used by durable cost budgets and settlements. */\n readonly estimateCostMicrousd?: RunCostEstimator | undefined;\n /** Closed trusted Tool failure reporting. Omission masks ambient observers at construction. */\n readonly toolFailureObserver?: RunToolFailureObserver | undefined;\n /** Milliseconds; default 5000. */\n readonly busyTimeout?: number | undefined;\n /** Milliseconds; default 25. */\n readonly observationPollInterval?: number | undefined;\n /** Default false. */\n readonly verifyOnOpen?: boolean | undefined;\n /** SQLite adapter fault injection (`ledger:*` / `append:*` locations); default none. */\n readonly storageFailpoint?: SqliteStorageFailpointHandler | undefined;\n /** Coordinator fault injection (`submit:*` / `terminalize:*` locations); default none. */\n readonly runtimeFailpoint?: DurableRuntimeFailpointHandler | undefined;\n /**\n * Reconciliation policy consulted for open ordinary Tool Calls before an Unknown Outcome is\n * recorded (durability §10, DUR-009). Defaults to the fail-closed `ToolReconciler.uncertain`:\n * with no registered policy, every open call stays Unknown and routes to the authorized\n * DUR-017 resolution path.\n */\n readonly toolReconciler?: Layer.Layer<ToolReconciler> | undefined;\n /** Host prompt preparation/compaction, acquired once with the runtime; default pass-through. */\n readonly runContext?:\n | Layer.Layer<RunContextPreparation, ContextError, ContextRequirements | Crypto.Crypto>\n | undefined;\n /**\n * Independent action-time Tool authority, acquired once with the runtime; default allow-all.\n * Construction errors and application dependencies remain in the assembled Layer's E and R.\n * The platform supplies Crypto to both extension Layers.\n */\n readonly toolAuthorization?:\n | Layer.Layer<\n RunToolAuthorization,\n AuthorizationError,\n AuthorizationRequirements | Crypto.Crypto\n >\n | undefined;\n}\n\n/** Built-in construction failures. `layer` also preserves supplied service Layers' errors. */\nexport type NodeDurableRuntimeInitializationError =\n | NodePlatformConfigError\n | SqliteStorageInitializationError;\n\n/** The services `NodeDurableRuntime.layer` provides. */\nexport type NodeDurableRuntimeServices =\n | DurableAgentRuntime\n | SubmissionLedger\n | ThreadStore\n | ScheduleStore\n | WakeScheduler\n | DurableRuntimeConfig\n | NodeDurableRuntimeConfig;\n\nconst decodeConfigValue = Schema.decodeUnknownEffect(NodeDurableRuntimeConfigValue);\n\nconst configFromOptions = (\n options: Omit<NodeDurableRuntimeOptions, \"runContext\" | \"toolAuthorization\">,\n): Effect.Effect<NodeDurableRuntimeConfigValue, NodePlatformConfigError> =>\n decodeConfigValue({\n filename: options.filename,\n deploymentId: options.deploymentId,\n producerId: options.producerId,\n ownershipLeaseDuration:\n options.ownershipLeaseDuration ?? Duration.toMillis(DEFAULT_OWNERSHIP_LEASE_DURATION),\n workerConcurrency: options.workerConcurrency ?? 1,\n wakeScanInterval: options.wakeScanInterval ?? 1_000,\n settlementPollInterval: options.settlementPollInterval ?? 500,\n leaseRenewalInterval: options.leaseRenewalInterval ?? 10_000,\n abortPollInterval: options.abortPollInterval ?? 500,\n busyTimeout: options.busyTimeout ?? 5_000,\n observationPollInterval: options.observationPollInterval ?? 25,\n verifyOnOpen: options.verifyOnOpen ?? false,\n }).pipe(\n Effect.mapError((error) =>\n NodePlatformConfigError.make({\n message: `Invalid Node durable runtime configuration: ${error.message}`,\n cause: error,\n }),\n ),\n );\n\n/** SQLite storage configuration derived from the single validated Node configuration. */\nconst sqliteStorageConfigLayer: Layer.Layer<SqliteStorageConfig, never, NodeDurableRuntimeConfig> =\n Layer.effect(SqliteStorageConfig)(\n Effect.gen(function* () {\n const config = yield* NodeDurableRuntimeConfig;\n\n return SqliteStorageConfigValue.make({\n observationPollInterval: config.observationPollInterval,\n busyTimeout: config.busyTimeout,\n ownershipLeaseDuration: config.ownershipLeaseDuration,\n verifyOnOpen: config.verifyOnOpen,\n });\n }),\n );\n\n/** Thread coordinator configuration derived from the single validated Node configuration. */\nconst durableRuntimeConfigLayer = (\n estimateCostMicrousd: RunCostEstimator | undefined,\n): Layer.Layer<DurableRuntimeConfig, never, NodeDurableRuntimeConfig> =>\n Layer.effect(DurableRuntimeConfig)(\n Effect.gen(function* () {\n const config = yield* NodeDurableRuntimeConfig;\n\n return DurableRuntimeConfig.make({\n deploymentId: config.deploymentId,\n producerId: config.producerId,\n settlementPollInterval: Duration.millis(config.settlementPollInterval),\n leaseRenewalInterval: Duration.millis(config.leaseRenewalInterval),\n abortPollInterval: Duration.millis(config.abortPollInterval),\n ...(estimateCostMicrousd === undefined ? {} : { estimateCostMicrousd }),\n });\n }),\n );\n\n/** Wake fallback-scan cadence derived from the single validated Node configuration. */\nconst wakeSchedulerConfigLayer: Layer.Layer<\n NodeWakeSchedulerConfig,\n never,\n NodeDurableRuntimeConfig\n> = Layer.effect(NodeWakeSchedulerConfig)(\n Effect.gen(function* () {\n const config = yield* NodeDurableRuntimeConfig;\n\n return { scanInterval: Duration.millis(config.wakeScanInterval) };\n }),\n);\n\nconst releaseTrackedOwnership = (\n ledger: SubmissionLedger[\"Service\"],\n registry: Ref.Ref<ReadonlyMap<SubmissionId, OwnershipToken>>,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n const tracked = yield* Ref.getAndSet(registry, new Map<SubmissionId, OwnershipToken>());\n\n for (const [submissionId, ownershipToken] of tracked) {\n yield* ledger\n .releaseOwnership(ReleaseOwnershipRequest.make({ submissionId, ownershipToken }))\n .pipe(\n Effect.catchTags({\n // A newer epoch already owns (or settled) the lane: nothing left to drain.\n OwnershipLost: () => Effect.void,\n // Drain is best-effort by design: the lease still expires and the durability protocol,\n // not graceful shutdown, provides correctness (DEPLOY-006).\n LedgerError: (error) =>\n Effect.logWarning(\"Ownership drain failed; the lease will expire instead\", error),\n }),\n );\n }\n });\n\n/**\n * Shutdown-drain decorator for a `SubmissionLedger` (deployment §6 step 6): every ownership\n * period granted through this Layer is tracked — claims start tracking, renewals follow token\n * rotation, releases and settlement finalizations stop it — and every ownership still held when\n * the Layer's Scope closes is released so another host can claim the lane immediately instead of\n * waiting for lease expiry. The drain is a liveness courtesy only; producer-epoch fencing remains\n * the correctness authority (DUR-006), and a forced kill simply falls back to lease expiry.\n */\nexport const ownershipDrainLayer: Layer.Layer<SubmissionLedger, never, SubmissionLedger> =\n Layer.effect(SubmissionLedger)(\n Effect.gen(function* () {\n const ledger = yield* SubmissionLedger;\n\n const registry = yield* Ref.make<ReadonlyMap<SubmissionId, OwnershipToken>>(\n new Map<SubmissionId, OwnershipToken>(),\n );\n\n const track = (submissionId: SubmissionId, ownershipToken: OwnershipToken) =>\n Ref.update(registry, (tracked) => new Map(tracked).set(submissionId, ownershipToken));\n\n const untrack = (submissionId: SubmissionId) =>\n Ref.update(registry, (tracked) => {\n const next = new Map(tracked);\n\n next.delete(submissionId);\n\n return next;\n });\n\n yield* Effect.addFinalizer(() => releaseTrackedOwnership(ledger, registry));\n\n return SubmissionLedger.of({\n capabilities: ledger.capabilities,\n admit: ledger.admit,\n markReady: ledger.markReady,\n lookup: ledger.lookup,\n // The S2 subagent ops forward untouched: none of them grants an ownership period, so\n // the drain has nothing to track for them (`suspend` below already stops tracking the\n // waitingForChild ownership period the moment it ends).\n resolveAdmission: ledger.resolveAdmission,\n recordChildSettled: ledger.recordChildSettled,\n reserveChildBudget: ledger.reserveChildBudget,\n attachChildToReservation: ledger.attachChildToReservation,\n beginChildBudgetRelease: ledger.beginChildBudgetRelease,\n releaseChildBudget: ledger.releaseChildBudget,\n claim: (request) =>\n ledger\n .claim(request)\n .pipe(\n Effect.tap((claimed) =>\n claimed._tag === \"Some\"\n ? track(claimed.value.submissionId, claimed.value.ownershipToken)\n : Effect.void,\n ),\n ),\n renewOwnership: (request) =>\n ledger.renewOwnership(request).pipe(\n Effect.tap((renewal) => track(request.submissionId, renewal.ownershipToken)),\n Effect.tapError((error) =>\n error._tag === \"OwnershipLost\" ? untrack(request.submissionId) : Effect.void,\n ),\n ),\n releaseOwnership: (request) =>\n ledger.releaseOwnership(request).pipe(\n Effect.tap(() => untrack(request.submissionId)),\n Effect.tapError((error) =>\n error._tag === \"OwnershipLost\" ? untrack(request.submissionId) : Effect.void,\n ),\n ),\n markInputApplied: ledger.markInputApplied,\n reserveSettlement: ledger.reserveSettlement,\n finalizeSettlement: (request) =>\n ledger.finalizeSettlement(request).pipe(Effect.tap(() => untrack(request.submissionId))),\n requestAbort: ledger.requestAbort,\n claimJoining: ledger.claimJoining,\n markJoined: ledger.markJoined,\n revertJoining: ledger.revertJoining,\n // Suspension ends the ownership period by contract, so the drain stops tracking it.\n suspend: (request) =>\n ledger.suspend(request).pipe(Effect.tap(() => untrack(request.submissionId))),\n recordApprovalDecision: ledger.recordApprovalDecision,\n markUnknown: ledger.markUnknown,\n recordUnknownResolution: ledger.recordUnknownResolution,\n scanNonterminal: ledger.scanNonterminal,\n loadRecoverySnapshot: ledger.loadRecoverySnapshot,\n });\n }),\n );\n\n/**\n * The DN Layer assembly (deployment §12: a Layer-assembly library, not an app entrypoint).\n * `layer(options)` decodes the configuration, opens ONE SQLite database serving both the\n * Thread Log and the Submission Ledger (so claims fence the same producer epochs), wires\n * the Node wake scheduler with its ledger-scan fallback, wraps the ledger with the shutdown\n * ownership drain, defaults the Tool reconciliation policy to the fail-closed\n * `ToolReconciler.uncertain` (override via `options.toolReconciler`), and provides a ready\n * `DurableAgentRuntime` on top. Storage compatibility is\n * verified during construction: an incompatible database file fails the Layer with\n * `SqliteStorageCompatibilityError` before anything is mutated (DEPLOY-008).\n */\nexport class NodeDurableRuntime {\n /** Validated configuration Layer; fails typed when the supplied options are out of bounds. */\n static configLayer<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n >(\n options: NodeDurableRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements\n >,\n ): Layer.Layer<NodeDurableRuntimeConfig, NodePlatformConfigError> {\n return Layer.effect(NodeDurableRuntimeConfig)(configFromOptions(options));\n }\n\n /** The full DN runtime stack over one SQLite file. */\n static layer<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n >(\n options: NodeDurableRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements\n >,\n ): Layer.Layer<\n NodeDurableRuntimeServices,\n NodeDurableRuntimeInitializationError | ContextError | AuthorizationError,\n Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>\n > {\n return Layer.unwrap(\n Effect.map(configFromOptions(options), (config) => {\n const nodeConfigLayer = Layer.succeed(NodeDurableRuntimeConfig)(config);\n\n const infrastructure = Layer.mergeAll(\n sqliteStorageConfigLayer,\n storageFailpointLayer({\n filename: config.filename,\n failpoint: options.storageFailpoint,\n }),\n SqliteClient.layer({ filename: config.filename }),\n NodeCrypto.layer,\n );\n\n const runtimeFailpointLayer =\n options.runtimeFailpoint === undefined\n ? DurableRuntimeFailpoint.layer\n : Layer.succeed(DurableRuntimeFailpoint)({ hit: options.runtimeFailpoint });\n\n const reconcilerLayer = options.toolReconciler ?? ToolReconciler.uncertain;\n\n const observerLayer =\n options.toolFailureObserver === undefined\n ? Layer.succeed(CurrentToolFailureObserver)(undefined)\n : toolFailureObserverLayer(options.toolFailureObserver);\n\n const ports = Layer.mergeAll(\n threadStoreLayer,\n scheduleStoreLayer,\n nodeWakeSchedulerLayer.pipe(\n Layer.provideMerge(ownershipDrainLayer.pipe(Layer.provide(submissionLedgerLayer))),\n ),\n );\n\n return DurableAgentRuntime.layerWithServices.pipe(\n Layer.provideMerge(\n Layer.mergeAll(ports, durableRuntimeConfigLayer(options.estimateCostMicrousd)),\n ),\n Layer.provide(\n Layer.mergeAll(\n wakeSchedulerConfigLayer,\n runtimeFailpointLayer,\n reconcilerLayer,\n observerLayer,\n ),\n ),\n Layer.provideMerge(infrastructure),\n Layer.provideMerge(nodeConfigLayer),\n Layer.provide(\n Layer.mergeAll(\n options.runContext ?? RunContextPreparationPassthrough,\n options.toolAuthorization ?? RunToolAuthorization.allowAll,\n ).pipe(Layer.provide(NodeCrypto.layer)),\n ),\n );\n }),\n );\n }\n}\n","import type { ThreadId, SubmissionId } from \"@effect-agent/core\";\nimport {\n compileRegistrations,\n type AgentRegistration,\n DurableAgentRuntime,\n type AbortCommand,\n type AbortIntent,\n type CanonicalRecordEnvelope,\n type ThreadNotMaterialized,\n type ThreadStoreError,\n type DurableAbortFailure,\n type DurableAwaitFailure,\n type ResolvedBinding,\n type DurableBindingFailure,\n type DurableExplainFailure,\n type DurableObserveOptions,\n type DurableObligationFailure,\n type DurableRetryFailure,\n type DurableSubmitAgent,\n type DurableSubmitFailure,\n type DurableSubmitOptions,\n type DurableVerifyFailure,\n type DurableWorkerFailure,\n type IntegrityReport,\n type ObligationReport,\n type ObligationThresholds,\n type OperationDenied,\n type Receipt,\n type RecoveryExplanation,\n type RecoveryReport,\n type RetryCommand,\n type Settlement,\n} from \"@effect-agent/thread\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { type Stream, Context, type Crypto, Effect, Layer, Ref, Schema } from \"effect\";\n\nimport {\n NodeDurableRuntime,\n NodeDurableRuntimeConfig,\n type NodeDurableRuntimeInitializationError,\n type NodeDurableRuntimeOptions,\n type NodeDurableRuntimeServices,\n} from \"./layers.ts\";\n\n/**\n * Admission is not open on this host: it is shutting down (deployment §6 step 1, DEPLOY-005).\n * Accepted work is unaffected — only NEW admissions are refused.\n */\nexport class AdmissionClosed extends Schema.TaggedError<AdmissionClosed>()(\"AdmissionClosed\", {\n message: Schema.String,\n}) {}\n\nconst makeHost = (bindings: ReadonlyArray<ResolvedBinding>) =>\n Effect.gen(function* () {\n const runtime = yield* DurableAgentRuntime;\n const config = yield* NodeDurableRuntimeConfig;\n\n // Startup gate (deployment §5, plan §host): configuration decoding and storage compatibility\n // already gated this Layer's dependencies; the last gate before admission opens is recovering\n // EVERY nonterminal Submission. Work needing a live Agent Binding is reported `deferred` and\n // stays a visible obligation for `runWorkers`; lanes durably blocked on an Unknown Outcome are\n // reported `unknown` and wait for the authorized `resolveUnknown` path (DUR-017) — they consume\n // no worker permit while the settlement obligation stays owed.\n const startupRecovery = yield* runtime.runRecovery;\n\n const admission = yield* Ref.make(true);\n\n // Shutdown step 1 (DEPLOY-005): admission closes before the ownership drain in the runtime\n // Layer below releases claims and before the stores close in reverse acquisition order.\n yield* Effect.addFinalizer(() => Ref.set(admission, false));\n\n const requireAdmission: Effect.Effect<void, AdmissionClosed> = Ref.get(admission).pipe(\n Effect.flatMap((open) =>\n open\n ? Effect.void\n : Effect.fail(\n AdmissionClosed.make({ message: \"The host is shutting down; admission is closed.\" }),\n ),\n ),\n );\n\n const submit = <InputSchema extends Schema.Top>(\n agent: DurableSubmitAgent<InputSchema>,\n input: InputSchema[\"Type\"],\n options: DurableSubmitOptions,\n ): Effect.Effect<\n Receipt,\n AdmissionClosed | DurableSubmitFailure,\n InputSchema[\"EncodingServices\"]\n > => requireAdmission.pipe(Effect.andThen(runtime.submit(agent, input, options)));\n\n const runWorkers = <A, E, R>(worker: Effect.Effect<A, E, R>): Effect.Effect<void, E, R> =>\n Effect.forEach(\n Array.from({ length: config.workerConcurrency }, (_, index) => index),\n () => worker,\n { concurrency: \"unbounded\", discard: true },\n );\n\n // S2 multi-binding pool: every claimed head resolves its exact registered Binding through\n // the host's exact registrations, so one bounded\n // pool serves parent and child lanes — the spec §12 smallest-pool suspension/wakeup proof\n // runs `workerConcurrency: 1` over exactly this loop.\n const runResolvedWorkers = runWorkers(runtime.runResolvedWorker(bindings));\n\n return NodeDurableHost.of({\n startupRecovery,\n admissionOpen: Ref.get(admission),\n submit,\n awaitSettlement: runtime.awaitSettlement,\n observe: runtime.observe,\n abort: runtime.abort,\n explain: runtime.explain,\n explainThread: runtime.explainThread,\n verify: runtime.verify,\n retry: runtime.retry,\n wake: runtime.wake,\n scanObligations: runtime.scanObligations,\n runWorkers,\n runResolvedWorkers,\n });\n });\n\n/**\n * Operational host lifecycle for the DN runtime (deployment §2/§5/§6).\n *\n * Startup gates run during Layer construction, so the service existing implies readiness:\n * configuration was schema-decoded, the SQLite file passed the exact-version compatibility check,\n * and every nonterminal Submission went through one full recovery pass BEFORE admission opened.\n * `startupRecovery` is the auditable evidence of that reconciliation pass.\n *\n * Shutdown runs in reverse Layer order when the owning Scope closes: `submit` starts refusing\n * with `AdmissionClosed` first, then the runtime Layer's ownership drain releases every claim\n * still held so another host can take over the lanes immediately, then the SQLite resources\n * close. Forced termination at any point stays safe — the durability protocol, not graceful\n * shutdown, provides correctness (DEPLOY-006).\n */\nexport class NodeDurableHost extends Context.Service<\n NodeDurableHost,\n {\n /**\n * The recovery decisions executed (or deferred) by this host's startup reconciliation.\n * Reports with the `unknown` disposition identify lanes blocked on Unknown Outcomes that\n * only the authorized DUR-017 resolution path can release.\n */\n readonly startupRecovery: ReadonlyArray<RecoveryReport>;\n /** Admission-role readiness (deployment §7): true until shutdown begins. */\n readonly admissionOpen: Effect.Effect<boolean>;\n /** `DurableAgentRuntime.submit` behind the host admission gate. */\n readonly submit: <InputSchema extends Schema.Top>(\n agent: DurableSubmitAgent<InputSchema>,\n input: InputSchema[\"Type\"],\n options: DurableSubmitOptions,\n ) => Effect.Effect<\n Receipt,\n AdmissionClosed | DurableSubmitFailure,\n InputSchema[\"EncodingServices\"]\n >;\n readonly awaitSettlement: (receipt: Receipt) => Effect.Effect<Settlement, DurableAwaitFailure>;\n readonly observe: (\n receipt: Receipt,\n options?: DurableObserveOptions,\n ) => Stream.Stream<\n CanonicalRecordEnvelope,\n ThreadStoreError | ThreadNotMaterialized | OperationDenied\n >;\n readonly abort: (command: AbortCommand) => Effect.Effect<AbortIntent, DurableAbortFailure>;\n /** `DurableAgentRuntime.explain` — read-only recovery explanation of one Submission (P7). */\n readonly explain: (\n submissionId: SubmissionId,\n ) => Effect.Effect<RecoveryExplanation, DurableExplainFailure>;\n /** `DurableAgentRuntime.explainThread` — explain every nonterminal lane member. */\n readonly explainThread: (\n threadId: ThreadId,\n ) => Effect.Effect<ReadonlyArray<RecoveryExplanation>, DurableExplainFailure>;\n /** `DurableAgentRuntime.verify` — read-only integrity checks, never a repair (P7). */\n readonly verify: (threadId: ThreadId) => Effect.Effect<IntegrityReport, DurableVerifyFailure>;\n /** `DurableAgentRuntime.retry` — audited single-Submission re-drive with typed refusals. */\n readonly retry: (command: RetryCommand) => Effect.Effect<RecoveryReport, DurableRetryFailure>;\n /** `DurableAgentRuntime.wake` — the documented operator liveness nudge for one lane. */\n readonly wake: (threadId: ThreadId) => Effect.Effect<void, OperationDenied>;\n /** `DurableAgentRuntime.scanObligations` — the scan-based DUR-017/OPS-001 report. */\n readonly scanObligations: (\n thresholds: ObligationThresholds,\n ) => Effect.Effect<ObligationReport, DurableObligationFailure>;\n /**\n * Run `workerConcurrency` copies of the given worker effect (typically\n * `DurableAgentRuntime.runWorker(agent)`) until the caller's Scope interrupts them. The\n * bound is the validated finite configuration value; the host never forks daemon fibers.\n */\n readonly runWorkers: <A, E, R>(worker: Effect.Effect<A, E, R>) => Effect.Effect<void, E, R>;\n /**\n * Run `workerConcurrency` copies of `DurableAgentRuntime.runResolvedWorker` over the host's\n * registered Bindings (S2): every claimed head resolves its exact stored Binding before any\n * code runs (SUB-023), so one bounded pool serves parent and attached-child lanes.\n */\n readonly runResolvedWorkers: Effect.Effect<void, DurableWorkerFailure | DurableBindingFailure>;\n }\n>()(\"@effect-agent/platform-node/NodeDurableHost\") {\n /**\n * Compile typed registrations and acquire the complete host in one Layer Scope.\n * Node supplies Crypto; model, tool, instruction, and schema services remain required.\n * Startup recovery and shutdown gates are unchanged. Workers start only when the caller\n * runs runResolvedWorkers; this constructor never starts a background worker.\n */\n static layerRegistered<\n const Entries extends ReadonlyArray<AgentRegistration>,\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n >(\n registrations: Entries,\n options: NodeDurableRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements\n >,\n ) {\n return Layer.unwrap(\n Effect.map(compileRegistrations(registrations), (bindings) =>\n NodeDurableHost.layerStack({ ...options, bindings }),\n ),\n ).pipe(Layer.provide(NodeCrypto.layer));\n }\n\n /**\n * Host gates over an assembled `NodeDurableRuntime` stack. Bindings must carry the exact\n * digests stored by submitters. Omission registers no Agents, so resolved work fails closed.\n */\n static readonly layer = (\n bindings: ReadonlyArray<ResolvedBinding> = [],\n ): Layer.Layer<\n NodeDurableHost,\n DurableWorkerFailure,\n DurableAgentRuntime | NodeDurableRuntimeConfig\n > => Layer.effect(NodeDurableHost)(makeHost(bindings));\n\n /** The complete DN host: `NodeDurableRuntime.layer(options)` plus the host lifecycle gates. */\n static layerStack<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n >(\n options: NodeDurableRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements\n > & { readonly bindings?: ReadonlyArray<ResolvedBinding> },\n ): Layer.Layer<\n NodeDurableHost | NodeDurableRuntimeServices,\n | DurableWorkerFailure\n | NodeDurableRuntimeInitializationError\n | ContextError\n | AuthorizationError,\n Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>\n > {\n const { bindings = [], ...runtimeOptions } = options;\n\n return NodeDurableHost.layer(bindings).pipe(\n Layer.provideMerge(NodeDurableRuntime.layer(runtimeOptions)),\n );\n }\n}\n","import type { AgentId } from \"@effect-agent/core\";\nimport {\n type DurableSubmitAgent,\n type EventSources,\n type SubscriptionInputBindings,\n PersistedJson,\n type PreparedInput,\n PreparedInputAdmission,\n type ScheduledEnvelope,\n ScheduledInputAdmission,\n ScheduledInputRetryable,\n type SubscriptionAuthorizer,\n SubscriptionDriver,\n type SubscriptionError,\n SubscriptionIntake,\n type SubscriptionLimits,\n type SubscriptionStoreFailure,\n SubscriptionStore,\n Subscriptions,\n defaultSubscriptionLimits,\n ScheduleStorageError,\n} from \"@effect-agent/thread\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { Cause, Duration, Effect, Exit, Layer, Option } from \"effect\";\n\nimport { NodeDurableHost } from \"./host.ts\";\n\nconst passthroughSubmitAgent = (agentId: AgentId): DurableSubmitAgent<typeof PersistedJson> => ({\n definition: { id: agentId, input: PersistedJson },\n});\n\nconst ambiguous = (): ScheduledInputRetryable =>\n ScheduledInputRetryable.make({ reason: \"ambiguous\" });\n\nconst corrupt = (operation: string): ScheduleStorageError =>\n ScheduleStorageError.make({ operation, reason: \"corrupt\" });\n\n/** Ordinary prepared admission through the Scope-owned Node host gate. */\nexport const nodePreparedInputAdmissionLayer: Layer.Layer<\n PreparedInputAdmission,\n never,\n NodeDurableHost\n> = Layer.effect(\n PreparedInputAdmission,\n Effect.gen(function* () {\n const host = yield* NodeDurableHost;\n\n return PreparedInputAdmission.of({\n submit: (envelope) =>\n host\n .submit(passthroughSubmitAgent(envelope.agentId), envelope.input, {\n threadId: envelope.threadId,\n principal: envelope.deliveryPrincipal,\n idempotencyKey: envelope.admissionKey,\n definitions: envelope.definitions,\n })\n .pipe(\n Effect.catchTags({\n AdmissionClosed: () =>\n Effect.fail(ScheduledInputRetryable.make({ reason: \"host-closed\" })),\n AgentInputError: () => Effect.fail(corrupt(\"prepared admission input\")),\n AdmissionConflict: () => Effect.fail(corrupt(\"prepared admission conflict\")),\n DigestError: () => Effect.fail(ambiguous()),\n LedgerError: () => Effect.fail(ambiguous()),\n ThreadStoreError: () => Effect.fail(ambiguous()),\n ThreadNotMaterialized: () => Effect.fail(ambiguous()),\n AppendConflict: () => Effect.fail(ambiguous()),\n FenceRejected: () => Effect.fail(ambiguous()),\n DurableRuntimeFailpointError: () => Effect.fail(ambiguous()),\n }),\n ),\n });\n }),\n);\n\nconst preparedFromSchedule = (envelope: ScheduledEnvelope): PreparedInput => ({\n schemaVersion: 1,\n threadId: envelope.threadId,\n deliveryPrincipal: envelope.deliveryPrincipal,\n agentId: envelope.agentId,\n definitions: envelope.definitions,\n input: envelope.input,\n inputDigest: envelope.inputDigest,\n admissionKey: envelope.admissionKey,\n authorization: envelope.authorization,\n});\n\n/** Compatibility adapter retaining the public scheduling admission port. */\nconst nodeScheduledInputAdmissionFromPreparedLayer: Layer.Layer<\n ScheduledInputAdmission,\n never,\n PreparedInputAdmission\n> = Layer.effect(\n ScheduledInputAdmission,\n Effect.map(PreparedInputAdmission, (admission) =>\n ScheduledInputAdmission.of({\n submit: (envelope) => admission.submit(preparedFromSchedule(envelope)),\n }),\n ),\n);\n\nexport const nodeScheduledInputAdmissionLayer: Layer.Layer<\n ScheduledInputAdmission,\n never,\n NodeDurableHost\n> = nodeScheduledInputAdmissionFromPreparedLayer.pipe(\n Layer.provide(nodePreparedInputAdmissionLayer),\n);\n\nconst reportPassFailure = (cause: Cause.Cause<SubscriptionStoreFailure>): Effect.Effect<boolean> =>\n Cause.hasInterruptsOnly(cause)\n ? Effect.interrupt\n : Effect.logWarning(\"Node subscription pass failed\").pipe(\n Effect.annotateLogs({\n failureTag: Option.match(Cause.findErrorOption(cause), {\n onNone: () => \"Defect\",\n onSome: (error) => error._tag,\n }),\n }),\n Effect.as(false),\n );\n\nconst nodeSubscriptionDriverLayer = (\n limits: SubscriptionLimits,\n): Layer.Layer<never, never, SubscriptionDriver | SubscriptionStore> =>\n Layer.effectDiscard(\n Effect.gen(function* () {\n const driver = yield* SubscriptionDriver;\n const store = yield* SubscriptionStore;\n\n const run = Effect.gen(function* () {\n while (true) {\n const passSucceeded = yield* driver.runDue.pipe(\n Effect.map((pass) => pass.failed === 0),\n Effect.catchCause(reportPassFailure),\n );\n\n if (!passSucceeded) {\n yield* Effect.sleep(Duration.millis(limits.retryMillis));\n continue;\n }\n\n const deadline = yield* store.nextDeadline.pipe(Effect.exit);\n\n if (Exit.isFailure(deadline)) {\n yield* reportPassFailure(deadline.cause);\n yield* Effect.sleep(Duration.millis(limits.retryMillis));\n continue;\n }\n\n const nowMillis = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n\n const delay =\n deadline.value === null\n ? limits.retryMillis\n : Math.max(1, Math.min(deadline.value - nowMillis, limits.retryMillis));\n\n yield* Effect.sleep(Duration.millis(delay));\n }\n });\n\n yield* Effect.forkScoped(run);\n }),\n );\n\nexport interface NodeSubscriptionsOptions {\n readonly limits?: SubscriptionLimits | undefined;\n}\n\n/**\n * One Scope-owned subscription partition in the sole process owning its SQLite database.\n * Indexed polling repairs restart and lost wake state; closing the Scope interrupts the driver.\n */\nexport class NodeSubscriptions {\n static layer(\n options: NodeSubscriptionsOptions = {},\n ): Layer.Layer<\n Subscriptions | SubscriptionIntake,\n SubscriptionError,\n | NodeDurableHost\n | SubscriptionStore\n | SubscriptionAuthorizer\n | EventSources\n | SubscriptionInputBindings\n > {\n const limits = options.limits ?? defaultSubscriptionLimits;\n\n const publicServices = Layer.merge(\n Subscriptions.layer(limits),\n SubscriptionIntake.layer(limits),\n );\n\n const driver = nodeSubscriptionDriverLayer(limits).pipe(\n Layer.provide(SubscriptionDriver.layer(limits)),\n );\n\n return Layer.merge(publicServices, driver).pipe(\n Layer.provide(nodePreparedInputAdmissionLayer),\n Layer.provide(NodeCrypto.layer),\n );\n }\n}\n","import {\n type ScheduleProcessFailure,\n type ScheduleAuthorizer,\n type SchedulingLimits,\n ScheduleStorageError,\n ScheduleStore,\n type ScheduleValidationError,\n ScheduleWake,\n Scheduling,\n ScheduleDriver,\n defaultSchedulingLimits,\n} from \"@effect-agent/thread\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { Cause, Duration, Effect, Layer, Option, PubSub, Result } from \"effect\";\n\nimport type { NodeDurableHost } from \"./host.ts\";\nimport { nodeScheduledInputAdmissionLayer } from \"./subscriptions.ts\";\n\n/** One bounded hint slot for the single supported Node scheduler. Indexed polling repairs loss. */\nexport const nodeScheduleWakeLayer: Layer.Layer<ScheduleWake> = Layer.effect(\n ScheduleWake,\n Effect.gen(function* () {\n const hints = yield* PubSub.sliding<void>(1);\n const subscription = yield* PubSub.subscribe(hints);\n\n yield* Effect.addFinalizer(() => PubSub.shutdown(hints));\n\n return ScheduleWake.of({\n notify: PubSub.publish(hints, undefined).pipe(Effect.asVoid),\n await: PubSub.take(subscription),\n });\n }),\n);\n\nconst reportPassFailure = (cause: Cause.Cause<ScheduleProcessFailure>): Effect.Effect<boolean> =>\n Cause.hasInterruptsOnly(cause)\n ? Effect.interrupt\n : Effect.logWarning(\"Node scheduling pass failed\").pipe(\n Effect.annotateLogs({\n failureTag: Option.match(Cause.findErrorOption(cause), {\n onNone: () => \"Defect\",\n onSome: (error) => error._tag,\n }),\n }),\n Effect.as(false),\n );\n\nconst nodeSchedulingDriverLayer = (\n limits: SchedulingLimits,\n): Layer.Layer<never, never, ScheduleDriver | ScheduleStore | ScheduleWake> =>\n Layer.effectDiscard(\n Effect.gen(function* () {\n const scheduling = yield* ScheduleDriver;\n const store = yield* ScheduleStore;\n const wake = yield* ScheduleWake;\n\n const run = Effect.gen(function* () {\n while (true) {\n const passSucceeded = yield* scheduling.runDue().pipe(\n Effect.map((pass) => pass.failed === 0),\n Effect.catchCause(reportPassFailure),\n );\n\n const deadlineResult = passSucceeded\n ? yield* store.nextDeadline().pipe(Effect.result)\n : Result.fail(\n ScheduleStorageError.make({ operation: \"driver pass\", reason: \"unavailable\" }),\n );\n\n if (Result.isFailure(deadlineResult) && passSucceeded) {\n yield* Effect.logWarning(\"Node scheduling deadline query failed\");\n }\n const nowMillis = yield* Effect.clockWith((clock) => clock.currentTimeMillis);\n\n const deadlineDelay =\n Result.isSuccess(deadlineResult) && deadlineResult.success !== null\n ? Math.max(0, deadlineResult.success - nowMillis)\n : limits.recoveryPollMillis;\n\n const delay = Math.min(deadlineDelay, limits.recoveryPollMillis);\n\n yield* Effect.raceFirst(wake.await, Effect.sleep(Duration.millis(delay)));\n }\n });\n\n yield* Effect.forkScoped(run);\n }),\n );\n\nexport interface NodeSchedulingOptions {\n readonly limits?: SchedulingLimits | undefined;\n}\n\n/**\n * Optional Scope-owned Node scheduling service and driver. The caller must provide the existing\n * host, its SQLite ScheduleStore, and an explicit ScheduleAuthorizer.\n */\nexport class NodeScheduling {\n static layer(\n options: NodeSchedulingOptions = {},\n ): Layer.Layer<\n Scheduling,\n ScheduleValidationError,\n NodeDurableHost | ScheduleStore | ScheduleAuthorizer\n > {\n const limits = options.limits ?? defaultSchedulingLimits;\n\n const schedulingWithDriver = nodeSchedulingDriverLayer(limits).pipe(\n Layer.provide(ScheduleDriver.layer(limits)),\n Layer.merge(Scheduling.layer(limits)),\n );\n\n return schedulingWithDriver.pipe(\n Layer.provide(\n Layer.mergeAll(nodeScheduledInputAdmissionLayer, nodeScheduleWakeLayer, NodeCrypto.layer),\n ),\n );\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,48 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@effect-agent/platform-node",
|
|
3
|
-
"version": "0.1.0-beta.41",
|
|
4
|
-
"exports": {
|
|
5
|
-
".": {
|
|
6
|
-
"types": "./dist/index.d.mts",
|
|
7
|
-
"default": "./dist/index.mjs"
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"@effect-agent/core": "0.1.0-beta.41",
|
|
12
|
-
"@effect-agent/engine": "0.1.0-beta.41",
|
|
13
|
-
"@effect-agent/storage-sqlite": "0.1.0-beta.41",
|
|
14
|
-
"@effect-agent/thread": "0.1.0-beta.41",
|
|
15
|
-
"@effect/platform-node": "4.0.0-rc.111",
|
|
16
|
-
"@effect/sql-sqlite-node": "4.0.0-rc.111"
|
|
17
|
-
},
|
|
18
|
-
"peerDependencies": {
|
|
19
|
-
"effect": "^4.0.0-rc.111"
|
|
20
|
-
},
|
|
21
|
-
"description": "Class DN layer assembly for Effect Agent: the durable Node/SQLite runtime host, wake scheduler, and ownership drain.",
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"repository": {
|
|
24
|
-
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/danieljvdm/effect-agent.git",
|
|
26
|
-
"directory": "packages/platform-node"
|
|
27
|
-
},
|
|
28
|
-
"files": [
|
|
29
|
-
"dist",
|
|
30
|
-
"src"
|
|
31
|
-
],
|
|
32
|
-
"type": "module",
|
|
33
|
-
"publishConfig": {
|
|
34
|
-
"access": "public"
|
|
35
|
-
},
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "vp pack",
|
|
38
|
-
"check": "tsc --noEmit -p tsconfig.json",
|
|
39
|
-
"test": "vp test --passWithNoTests"
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@effect-agent/capabilities": "0.1.0-beta.40",
|
|
43
|
-
"@effect/vitest": "4.0.0-rc.111",
|
|
44
|
-
"effect": "4.0.0-rc.111",
|
|
45
|
-
"typescript": "7.0.2",
|
|
46
|
-
"vite-plus": "0.3.0"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
{"name":"@effect-agent/platform-node","version":"0.1.0-beta.44","dependencies":{"@effect-agent/core":"0.1.0-beta.44","@effect-agent/engine":"0.1.0-beta.44","@effect-agent/storage-sqlite":"0.1.0-beta.44","@effect-agent/thread":"0.1.0-beta.44","@effect/platform-node":"4.0.0-rc.112","@effect/sql-sqlite-node":"4.0.0-rc.112"},"devDependencies":{"@effect-agent/capabilities":"0.1.0-beta.44","@effect/vitest":"4.0.0-rc.112","effect":"4.0.0-rc.112","typescript":"7.0.2","vite-plus":"0.3.0"},"peerDependencies":{"effect":"^4.0.0-rc.112"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"}},"description":"Class DN layer assembly for Effect Agent: the durable Node/SQLite runtime host, wake scheduler, and ownership drain.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/platform-node"},"files":["dist","src"],"type":"module","publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test --passWithNoTests"}}
|
package/src/host.ts
CHANGED
|
@@ -64,6 +64,7 @@ const makeHost = (bindings: ReadonlyArray<ResolvedBinding>) =>
|
|
|
64
64
|
const startupRecovery = yield* runtime.runRecovery;
|
|
65
65
|
|
|
66
66
|
const admission = yield* Ref.make(true);
|
|
67
|
+
|
|
67
68
|
// Shutdown step 1 (DEPLOY-005): admission closes before the ownership drain in the runtime
|
|
68
69
|
// Layer below releases claims and before the stores close in reverse acquisition order.
|
|
69
70
|
yield* Effect.addFinalizer(() => Ref.set(admission, false));
|
|
@@ -257,6 +258,7 @@ export class NodeDurableHost extends Context.Service<
|
|
|
257
258
|
Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>
|
|
258
259
|
> {
|
|
259
260
|
const { bindings = [], ...runtimeOptions } = options;
|
|
261
|
+
|
|
260
262
|
return NodeDurableHost.layer(bindings).pipe(
|
|
261
263
|
Layer.provideMerge(NodeDurableRuntime.layer(runtimeOptions)),
|
|
262
264
|
);
|
package/src/layers.ts
CHANGED
|
@@ -42,6 +42,7 @@ import { NodeWakeSchedulerConfig, nodeWakeSchedulerLayer } from "./wake-schedule
|
|
|
42
42
|
|
|
43
43
|
const PositiveMillis = Schema.Int.check(Schema.isGreaterThan(0));
|
|
44
44
|
const NonNegativeMillis = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
|
|
45
|
+
|
|
45
46
|
const WorkerConcurrency = Schema.Int.check(
|
|
46
47
|
Schema.isGreaterThanOrEqualTo(1),
|
|
47
48
|
Schema.isLessThanOrEqualTo(64),
|
|
@@ -207,6 +208,7 @@ const sqliteStorageConfigLayer: Layer.Layer<SqliteStorageConfig, never, NodeDura
|
|
|
207
208
|
Layer.effect(SqliteStorageConfig)(
|
|
208
209
|
Effect.gen(function* () {
|
|
209
210
|
const config = yield* NodeDurableRuntimeConfig;
|
|
211
|
+
|
|
210
212
|
return SqliteStorageConfigValue.make({
|
|
211
213
|
observationPollInterval: config.observationPollInterval,
|
|
212
214
|
busyTimeout: config.busyTimeout,
|
|
@@ -223,6 +225,7 @@ const durableRuntimeConfigLayer = (
|
|
|
223
225
|
Layer.effect(DurableRuntimeConfig)(
|
|
224
226
|
Effect.gen(function* () {
|
|
225
227
|
const config = yield* NodeDurableRuntimeConfig;
|
|
228
|
+
|
|
226
229
|
return DurableRuntimeConfig.make({
|
|
227
230
|
deploymentId: config.deploymentId,
|
|
228
231
|
producerId: config.producerId,
|
|
@@ -242,6 +245,7 @@ const wakeSchedulerConfigLayer: Layer.Layer<
|
|
|
242
245
|
> = Layer.effect(NodeWakeSchedulerConfig)(
|
|
243
246
|
Effect.gen(function* () {
|
|
244
247
|
const config = yield* NodeDurableRuntimeConfig;
|
|
248
|
+
|
|
245
249
|
return { scanInterval: Duration.millis(config.wakeScanInterval) };
|
|
246
250
|
}),
|
|
247
251
|
);
|
|
@@ -252,6 +256,7 @@ const releaseTrackedOwnership = (
|
|
|
252
256
|
): Effect.Effect<void> =>
|
|
253
257
|
Effect.gen(function* () {
|
|
254
258
|
const tracked = yield* Ref.getAndSet(registry, new Map<SubmissionId, OwnershipToken>());
|
|
259
|
+
|
|
255
260
|
for (const [submissionId, ownershipToken] of tracked) {
|
|
256
261
|
yield* ledger
|
|
257
262
|
.releaseOwnership(ReleaseOwnershipRequest.make({ submissionId, ownershipToken }))
|
|
@@ -280,16 +285,20 @@ export const ownershipDrainLayer: Layer.Layer<SubmissionLedger, never, Submissio
|
|
|
280
285
|
Layer.effect(SubmissionLedger)(
|
|
281
286
|
Effect.gen(function* () {
|
|
282
287
|
const ledger = yield* SubmissionLedger;
|
|
288
|
+
|
|
283
289
|
const registry = yield* Ref.make<ReadonlyMap<SubmissionId, OwnershipToken>>(
|
|
284
290
|
new Map<SubmissionId, OwnershipToken>(),
|
|
285
291
|
);
|
|
286
292
|
|
|
287
293
|
const track = (submissionId: SubmissionId, ownershipToken: OwnershipToken) =>
|
|
288
294
|
Ref.update(registry, (tracked) => new Map(tracked).set(submissionId, ownershipToken));
|
|
295
|
+
|
|
289
296
|
const untrack = (submissionId: SubmissionId) =>
|
|
290
297
|
Ref.update(registry, (tracked) => {
|
|
291
298
|
const next = new Map(tracked);
|
|
299
|
+
|
|
292
300
|
next.delete(submissionId);
|
|
301
|
+
|
|
293
302
|
return next;
|
|
294
303
|
});
|
|
295
304
|
|
|
@@ -403,6 +412,7 @@ export class NodeDurableRuntime {
|
|
|
403
412
|
return Layer.unwrap(
|
|
404
413
|
Effect.map(configFromOptions(options), (config) => {
|
|
405
414
|
const nodeConfigLayer = Layer.succeed(NodeDurableRuntimeConfig)(config);
|
|
415
|
+
|
|
406
416
|
const infrastructure = Layer.mergeAll(
|
|
407
417
|
sqliteStorageConfigLayer,
|
|
408
418
|
storageFailpointLayer({
|
|
@@ -412,15 +422,19 @@ export class NodeDurableRuntime {
|
|
|
412
422
|
SqliteClient.layer({ filename: config.filename }),
|
|
413
423
|
NodeCrypto.layer,
|
|
414
424
|
);
|
|
425
|
+
|
|
415
426
|
const runtimeFailpointLayer =
|
|
416
427
|
options.runtimeFailpoint === undefined
|
|
417
428
|
? DurableRuntimeFailpoint.layer
|
|
418
429
|
: Layer.succeed(DurableRuntimeFailpoint)({ hit: options.runtimeFailpoint });
|
|
430
|
+
|
|
419
431
|
const reconcilerLayer = options.toolReconciler ?? ToolReconciler.uncertain;
|
|
432
|
+
|
|
420
433
|
const observerLayer =
|
|
421
434
|
options.toolFailureObserver === undefined
|
|
422
435
|
? Layer.succeed(CurrentToolFailureObserver)(undefined)
|
|
423
436
|
: toolFailureObserverLayer(options.toolFailureObserver);
|
|
437
|
+
|
|
424
438
|
const ports = Layer.mergeAll(
|
|
425
439
|
threadStoreLayer,
|
|
426
440
|
scheduleStoreLayer,
|
|
@@ -428,6 +442,7 @@ export class NodeDurableRuntime {
|
|
|
428
442
|
Layer.provideMerge(ownershipDrainLayer.pipe(Layer.provide(submissionLedgerLayer))),
|
|
429
443
|
),
|
|
430
444
|
);
|
|
445
|
+
|
|
431
446
|
return DurableAgentRuntime.layerWithServices.pipe(
|
|
432
447
|
Layer.provideMerge(
|
|
433
448
|
Layer.mergeAll(ports, durableRuntimeConfigLayer(options.estimateCostMicrousd)),
|
package/src/scheduling.ts
CHANGED
|
@@ -22,7 +22,9 @@ export const nodeScheduleWakeLayer: Layer.Layer<ScheduleWake> = Layer.effect(
|
|
|
22
22
|
Effect.gen(function* () {
|
|
23
23
|
const hints = yield* PubSub.sliding<void>(1);
|
|
24
24
|
const subscription = yield* PubSub.subscribe(hints);
|
|
25
|
+
|
|
25
26
|
yield* Effect.addFinalizer(() => PubSub.shutdown(hints));
|
|
27
|
+
|
|
26
28
|
return ScheduleWake.of({
|
|
27
29
|
notify: PubSub.publish(hints, undefined).pipe(Effect.asVoid),
|
|
28
30
|
await: PubSub.take(subscription),
|
|
@@ -58,20 +60,25 @@ const nodeSchedulingDriverLayer = (
|
|
|
58
60
|
Effect.map((pass) => pass.failed === 0),
|
|
59
61
|
Effect.catchCause(reportPassFailure),
|
|
60
62
|
);
|
|
63
|
+
|
|
61
64
|
const deadlineResult = passSucceeded
|
|
62
65
|
? yield* store.nextDeadline().pipe(Effect.result)
|
|
63
66
|
: Result.fail(
|
|
64
67
|
ScheduleStorageError.make({ operation: "driver pass", reason: "unavailable" }),
|
|
65
68
|
);
|
|
69
|
+
|
|
66
70
|
if (Result.isFailure(deadlineResult) && passSucceeded) {
|
|
67
71
|
yield* Effect.logWarning("Node scheduling deadline query failed");
|
|
68
72
|
}
|
|
69
73
|
const nowMillis = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
|
|
74
|
+
|
|
70
75
|
const deadlineDelay =
|
|
71
76
|
Result.isSuccess(deadlineResult) && deadlineResult.success !== null
|
|
72
77
|
? Math.max(0, deadlineResult.success - nowMillis)
|
|
73
78
|
: limits.recoveryPollMillis;
|
|
79
|
+
|
|
74
80
|
const delay = Math.min(deadlineDelay, limits.recoveryPollMillis);
|
|
81
|
+
|
|
75
82
|
yield* Effect.raceFirst(wake.await, Effect.sleep(Duration.millis(delay)));
|
|
76
83
|
}
|
|
77
84
|
});
|
|
@@ -97,10 +104,12 @@ export class NodeScheduling {
|
|
|
97
104
|
NodeDurableHost | ScheduleStore | ScheduleAuthorizer
|
|
98
105
|
> {
|
|
99
106
|
const limits = options.limits ?? defaultSchedulingLimits;
|
|
107
|
+
|
|
100
108
|
const schedulingWithDriver = nodeSchedulingDriverLayer(limits).pipe(
|
|
101
109
|
Layer.provide(ScheduleDriver.layer(limits)),
|
|
102
110
|
Layer.merge(Scheduling.layer(limits)),
|
|
103
111
|
);
|
|
112
|
+
|
|
104
113
|
return schedulingWithDriver.pipe(
|
|
105
114
|
Layer.provide(
|
|
106
115
|
Layer.mergeAll(nodeScheduledInputAdmissionLayer, nodeScheduleWakeLayer, NodeCrypto.layer),
|
package/src/subscriptions.ts
CHANGED
|
@@ -44,6 +44,7 @@ export const nodePreparedInputAdmissionLayer: Layer.Layer<
|
|
|
44
44
|
PreparedInputAdmission,
|
|
45
45
|
Effect.gen(function* () {
|
|
46
46
|
const host = yield* NodeDurableHost;
|
|
47
|
+
|
|
47
48
|
return PreparedInputAdmission.of({
|
|
48
49
|
submit: (envelope) =>
|
|
49
50
|
host
|
|
@@ -140,6 +141,7 @@ const nodeSubscriptionDriverLayer = (
|
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
const deadline = yield* store.nextDeadline.pipe(Effect.exit);
|
|
144
|
+
|
|
143
145
|
if (Exit.isFailure(deadline)) {
|
|
144
146
|
yield* reportPassFailure(deadline.cause);
|
|
145
147
|
yield* Effect.sleep(Duration.millis(limits.retryMillis));
|
|
@@ -147,10 +149,12 @@ const nodeSubscriptionDriverLayer = (
|
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
const nowMillis = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
|
|
152
|
+
|
|
150
153
|
const delay =
|
|
151
154
|
deadline.value === null
|
|
152
155
|
? limits.retryMillis
|
|
153
156
|
: Math.max(1, Math.min(deadline.value - nowMillis, limits.retryMillis));
|
|
157
|
+
|
|
154
158
|
yield* Effect.sleep(Duration.millis(delay));
|
|
155
159
|
}
|
|
156
160
|
});
|
|
@@ -180,13 +184,16 @@ export class NodeSubscriptions {
|
|
|
180
184
|
| SubscriptionInputBindings
|
|
181
185
|
> {
|
|
182
186
|
const limits = options.limits ?? defaultSubscriptionLimits;
|
|
187
|
+
|
|
183
188
|
const publicServices = Layer.merge(
|
|
184
189
|
Subscriptions.layer(limits),
|
|
185
190
|
SubscriptionIntake.layer(limits),
|
|
186
191
|
);
|
|
192
|
+
|
|
187
193
|
const driver = nodeSubscriptionDriverLayer(limits).pipe(
|
|
188
194
|
Layer.provide(SubscriptionDriver.layer(limits)),
|
|
189
195
|
);
|
|
196
|
+
|
|
190
197
|
return Layer.merge(publicServices, driver).pipe(
|
|
191
198
|
Layer.provide(nodePreparedInputAdmissionLayer),
|
|
192
199
|
Layer.provide(NodeCrypto.layer),
|
package/src/wake-scheduler.ts
CHANGED
|
@@ -29,6 +29,7 @@ const makeWakeScheduler = Effect.gen(function* () {
|
|
|
29
29
|
const config = yield* NodeWakeSchedulerConfig;
|
|
30
30
|
const hints = yield* PubSub.sliding<ThreadId>(WAKE_BUFFER_CAPACITY);
|
|
31
31
|
const progress = yield* makeWakeSubscriptionHub;
|
|
32
|
+
|
|
32
33
|
yield* Effect.addFinalizer(() => PubSub.shutdown(hints));
|
|
33
34
|
|
|
34
35
|
/**
|
|
@@ -41,9 +42,11 @@ const makeWakeScheduler = Effect.gen(function* () {
|
|
|
41
42
|
).pipe(
|
|
42
43
|
Effect.map((snapshots) => {
|
|
43
44
|
const lanes = new Set<ThreadId>();
|
|
45
|
+
|
|
44
46
|
for (const snapshot of snapshots) {
|
|
45
47
|
lanes.add(snapshot.threadId);
|
|
46
48
|
}
|
|
49
|
+
|
|
47
50
|
return [...lanes];
|
|
48
51
|
}),
|
|
49
52
|
Effect.catch((error) =>
|