@effect-agent/platform-node 0.1.0-beta.8 → 0.1.0-beta.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/NodeDurableAgentRuntime-CFElFzNF.d.mts +179 -0
  2. package/dist/NodeDurableAgentRuntime.d.mts +2 -0
  3. package/dist/NodeDurableAgentRuntime.mjs +221 -0
  4. package/dist/NodeDurableAgentRuntime.mjs.map +1 -0
  5. package/dist/NodeDurableHost-BMSajuQn.mjs +194 -0
  6. package/dist/NodeDurableHost-BMSajuQn.mjs.map +1 -0
  7. package/dist/NodeDurableHost.d.mts +168 -0
  8. package/dist/NodeDurableHost.mjs +2 -0
  9. package/dist/NodeScheduling.d.mts +22 -0
  10. package/dist/NodeScheduling.mjs +59 -0
  11. package/dist/NodeScheduling.mjs.map +1 -0
  12. package/dist/NodeSubscriptions.d.mts +27 -0
  13. package/dist/NodeSubscriptions.mjs +74 -0
  14. package/dist/NodeSubscriptions.mjs.map +1 -0
  15. package/dist/NodeWakeScheduler-4ZeXYwPu.d.mts +27 -0
  16. package/dist/NodeWakeScheduler.d.mts +2 -0
  17. package/dist/NodeWakeScheduler.mjs +65 -0
  18. package/dist/NodeWakeScheduler.mjs.map +1 -0
  19. package/dist/NodeWorkflow.d.mts +27 -0
  20. package/dist/NodeWorkflow.mjs +159 -0
  21. package/dist/NodeWorkflow.mjs.map +1 -0
  22. package/dist/index.d.mts +6 -214
  23. package/dist/index.mjs +6 -286
  24. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  25. package/package.json +1 -45
  26. package/src/NodeDurableAgentRuntime.ts +596 -0
  27. package/src/NodeDurableHost.ts +361 -0
  28. package/src/NodeScheduling.ts +121 -0
  29. package/src/NodeSubscriptions.ts +162 -0
  30. package/src/{wake-scheduler.ts → NodeWakeScheduler.ts} +32 -17
  31. package/src/NodeWorkflow.ts +258 -0
  32. package/src/index.ts +5 -3
  33. package/src/internal/message-delivery.ts +53 -0
  34. package/src/internal/prepared-admission.ts +81 -0
  35. package/dist/index.mjs.map +0 -1
  36. package/src/host.ts +0 -215
  37. package/src/layers.ts +0 -381
@@ -0,0 +1,194 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { NodeDurableAgentRuntime, NodeDurableAgentRuntimeConfig } from "./NodeDurableAgentRuntime.mjs";
3
+ import "@effect-agent/core/Identifiers";
4
+ import "@effect-agent/thread/AgentRegistration";
5
+ import { DurableAgentRuntime } from "@effect-agent/thread/DurableAgentRuntime";
6
+ import { MessageDeliveryDriver, MessageDeliveryStore } from "@effect-agent/thread/MessageDelivery";
7
+ import { PersistedJson } from "@effect-agent/thread/Records";
8
+ import { ScheduleStorageError, ScheduledInputRefused, ScheduledInputRetryable } from "@effect-agent/thread/Schedule";
9
+ import "@effect-agent/thread/SubmissionLedger";
10
+ import "@effect-agent/thread/ThreadStore";
11
+ import { NodeCrypto } from "@effect/platform-node";
12
+ import { Cause, Clock, Context, Effect, Exit, Fiber, Layer, Option, Ref, Schema } from "effect";
13
+ import "@effect-agent/thread/Admin";
14
+ import "@effect-agent/thread/OperationAuthorizer";
15
+ import { PreparedInputAdmission } from "@effect-agent/thread/PreparedInputAdmission";
16
+ //#region src/internal/message-delivery.ts
17
+ const reportFailure = (cause) => Cause.hasInterruptsOnly(cause) ? Effect.interrupt : Effect.logWarning("Node message delivery pass failed").pipe(Effect.annotateLogs({ failureTag: Option.match(Cause.findErrorOption(cause), {
18
+ onNone: () => "Defect",
19
+ onSome: (failure) => failure._tag
20
+ }) }));
21
+ /** Caller-owned loop. Indexed scans repair absent hints even when both Threads have settled. */
22
+ const runNodeMessageDeliveries = Effect.fn("NodeMessageDelivery.run")(function* (scanInterval) {
23
+ const driver = yield* MessageDeliveryDriver;
24
+ const store = yield* MessageDeliveryStore;
25
+ while (true) {
26
+ const pass = yield* driver.runDue().pipe(Effect.exit);
27
+ if (Exit.isFailure(pass)) {
28
+ yield* reportFailure(pass.cause);
29
+ yield* Effect.sleep(scanInterval);
30
+ continue;
31
+ }
32
+ const deadline = yield* store.nextDeadline().pipe(Effect.exit);
33
+ if (Exit.isFailure(deadline)) {
34
+ yield* reportFailure(deadline.cause);
35
+ yield* Effect.sleep(scanInterval);
36
+ continue;
37
+ }
38
+ const nowMillis = yield* Clock.currentTimeMillis;
39
+ const delay = deadline.value === null ? scanInterval : Math.max(1, Math.min(deadline.value - nowMillis, scanInterval));
40
+ yield* Effect.sleep(delay);
41
+ }
42
+ });
43
+ //#endregion
44
+ //#region src/internal/prepared-admission.ts
45
+ const passthroughSubmitAgent = (agentId) => ({ definition: {
46
+ id: agentId,
47
+ input: PersistedJson
48
+ } });
49
+ const ambiguous = () => ScheduledInputRetryable.make({ reason: "ambiguous" });
50
+ const corrupt = (operation) => ScheduleStorageError.make({
51
+ operation,
52
+ reason: "corrupt"
53
+ });
54
+ /** Host-owned admission gate, available while the host's worker pool is being assembled. */
55
+ var NodeAdmission = class extends Context.Service()("@effect-agent/platform-node/internal/NodeAdmission") {};
56
+ /** Acquire the gated source once; worker callers cannot replace its admission authority. */
57
+ const makeNodePreparedInputAdmission = Effect.gen(function* () {
58
+ const host = yield* NodeAdmission;
59
+ return PreparedInputAdmission.of({
60
+ submissionStatus: (receipt) => host.submissionStatus(receipt).pipe(Effect.mapError(() => ScheduledInputRetryable.make({ reason: "storage" }))),
61
+ submit: (envelope) => host.submit(passthroughSubmitAgent(envelope.agentId), envelope.input, {
62
+ threadId: envelope.threadId,
63
+ principal: envelope.deliveryPrincipal,
64
+ idempotencyKey: envelope.admissionKey,
65
+ ...envelope.admissionGroup === void 0 ? {} : { admissionGroup: envelope.admissionGroup },
66
+ ...envelope.admissionFence === void 0 ? {} : { admissionFence: envelope.admissionFence },
67
+ ...envelope.workerAdmission === void 0 ? {} : { workerAdmission: envelope.workerAdmission },
68
+ ...envelope.messageAdmission === void 0 ? {} : { messageAdmission: envelope.messageAdmission },
69
+ definitions: envelope.definitions
70
+ }).pipe(Effect.catchTags({
71
+ AdmissionClosed: () => Effect.fail(ScheduledInputRetryable.make({ reason: "host-closed" })),
72
+ AgentInputError: () => Effect.fail(corrupt("prepared admission input")),
73
+ AdmissionConflict: () => Effect.fail(corrupt("prepared admission conflict")),
74
+ DigestError: () => Effect.fail(ambiguous()),
75
+ AdmissionPolicyError: (error) => error.reason === "refused" ? ScheduledInputRefused.make({ code: error.code }) : ScheduledInputRetryable.make({ reason: error.reason === "occupied" ? "capacity" : "storage" }),
76
+ LedgerError: () => ScheduledInputRetryable.make({ reason: "storage" }),
77
+ ThreadStoreError: () => Effect.fail(ambiguous()),
78
+ ThreadNotMaterialized: () => Effect.fail(ambiguous()),
79
+ AppendConflict: () => Effect.fail(ambiguous()),
80
+ FenceRejected: () => Effect.fail(ambiguous()),
81
+ DurableRuntimeFailpointError: () => Effect.fail(ambiguous())
82
+ }))
83
+ });
84
+ });
85
+ //#endregion
86
+ //#region src/NodeDurableHost.ts
87
+ var NodeDurableHost_exports = /* @__PURE__ */ __exportAll({
88
+ AdmissionClosed: () => AdmissionClosed,
89
+ NodeDurableHost: () => NodeDurableHost,
90
+ layer: () => layer,
91
+ run: () => run
92
+ });
93
+ /**
94
+ * Admission is not open on this host: it is shutting down (deployment §6 step 1, DEPLOY-005).
95
+ * Accepted work is unaffected — only NEW admissions are refused.
96
+ */
97
+ var AdmissionClosed = class extends Schema.TaggedError()("AdmissionClosed", { message: Schema.String }) {};
98
+ const makeHost = Effect.fn("NodeDurableHost.make")(function* (startWorkers) {
99
+ const runtime = yield* DurableAgentRuntime;
100
+ const config = yield* NodeDurableAgentRuntimeConfig;
101
+ const startupRecovery = yield* runtime.runRecovery;
102
+ const admission = yield* Ref.make(true);
103
+ const requireAdmission = Ref.get(admission).pipe(Effect.flatMap((open) => open ? Effect.void : Effect.fail(AdmissionClosed.make({ message: "The host is shutting down; admission is closed." }))));
104
+ const submit = (agent, input, options) => requireAdmission.pipe(Effect.andThen(runtime.submit(agent, input, options)));
105
+ const deliveryServices = yield* Effect.context();
106
+ const deliveryContext = yield* Layer.build(MessageDeliveryDriver.layer({
107
+ batchSize: 100,
108
+ concurrency: Math.min(config.workerConcurrency, 32)
109
+ }).pipe(Layer.provide(NodeCrypto.layer), Layer.provide(Layer.effect(PreparedInputAdmission, makeNodePreparedInputAdmission).pipe(Layer.provide(Layer.succeed(NodeAdmission, {
110
+ submit,
111
+ submissionStatus: runtime.submissionStatus
112
+ }))))));
113
+ const runDeliveries = runNodeMessageDeliveries(config.wakeScanInterval).pipe(Effect.provide(Context.merge(deliveryServices, deliveryContext)));
114
+ const runWorkers = (worker) => Effect.scoped(Effect.raceFirst(Effect.forEach(Array.from({ length: config.workerConcurrency }, (_, index) => index), () => worker, {
115
+ concurrency: "unbounded",
116
+ discard: true
117
+ }), runDeliveries));
118
+ const runResolvedWorkers = runWorkers(runtime.runResolvedWorker);
119
+ const run = startWorkers ? Fiber.join(yield* runResolvedWorkers.pipe(Effect.onExit(() => Ref.set(admission, false)), Effect.forkScoped)) : runResolvedWorkers;
120
+ yield* Effect.addFinalizer(() => Ref.set(admission, false));
121
+ return NodeDurableHost.of({
122
+ startupRecovery,
123
+ admissionOpen: Ref.get(admission),
124
+ submit,
125
+ awaitSettlement: runtime.awaitSettlement,
126
+ submissionStatus: runtime.submissionStatus,
127
+ observe: runtime.observe,
128
+ abort: runtime.abort,
129
+ explain: runtime.explain,
130
+ explainThread: runtime.explainThread,
131
+ verify: runtime.verify,
132
+ retry: runtime.retry,
133
+ wake: runtime.wake,
134
+ scanObligations: runtime.scanObligations,
135
+ runWorkers,
136
+ run,
137
+ runResolvedWorkers: run
138
+ });
139
+ });
140
+ /**
141
+ * Operational host service. Prefer the module's `layer` and `run` for managed workers.
142
+ * The static constructors on this class retain explicit, manual worker ownership.
143
+ *
144
+ * Startup gates run during Layer construction, so the service existing implies readiness:
145
+ * configuration was schema-decoded, the SQLite file passed the exact-version compatibility check,
146
+ * and every nonterminal Submission went through one full recovery pass BEFORE admission opened.
147
+ * `startupRecovery` is the auditable evidence of that reconciliation pass.
148
+ *
149
+ * Shutdown runs in reverse Layer order when the owning Scope closes: `submit` starts refusing
150
+ * with `AdmissionClosed` first, then the runtime Layer's ownership drain releases every claim
151
+ * still held so another host can take over the lanes immediately, then the SQLite resources
152
+ * close. Forced termination at any point stays safe — the durability protocol, not graceful
153
+ * shutdown, provides correctness (DEPLOY-006).
154
+ */
155
+ var NodeDurableHost = class NodeDurableHost extends Context.Service()("@effect-agent/platform-node/NodeDurableHost") {
156
+ /**
157
+ * Compile typed registrations and acquire the complete host in one Layer Scope.
158
+ * Node supplies Crypto; model, tool, instruction, and schema services remain required.
159
+ * Startup recovery and shutdown gates are unchanged. Workers start only when the caller
160
+ * runs runResolvedWorkers; this constructor never starts a background worker.
161
+ */
162
+ static layerRegistered(registrations, options) {
163
+ return NodeDurableHost.layer.pipe(Layer.provideMerge(NodeDurableAgentRuntime.layerRegistered(registrations, options)));
164
+ }
165
+ /**
166
+ * Host gates over an assembled `NodeDurableAgentRuntime` stack. The runtime Layer owns
167
+ * executable registrations; omission registers no Agents, so resolved work fails closed.
168
+ */
169
+ static layer = Layer.effect(NodeDurableHost)(makeHost(false));
170
+ /** The complete DN host: `NodeDurableAgentRuntime.layer(options)` plus the host lifecycle gates. */
171
+ static layerStack(options) {
172
+ const { bindings = [], ...runtimeOptions } = options;
173
+ return NodeDurableHost.layer.pipe(Layer.provideMerge(NodeDurableAgentRuntime.layerWithBindings(bindings, runtimeOptions)));
174
+ }
175
+ };
176
+ /**
177
+ * Acquire a complete Node host and start one bounded, scoped worker pool after recovery.
178
+ * Provide model, tool, instruction, and schema dependencies to this Layer. Reusing the Layer
179
+ * shares the same pool. A worker failure closes admission; observe it with `run` at the process
180
+ * boundary so the application exits and releases the host instead of remaining idle.
181
+ */
182
+ const layer = (registrations, options) => Layer.effect(NodeDurableHost)(makeHost(true)).pipe(Layer.provideMerge(NodeDurableAgentRuntime.layerRegistered(registrations, options)));
183
+ /**
184
+ * Supervise the host's existing workers without starting another pool. Use with
185
+ * `Effect.provide(HostLive)` and `NodeRuntime.runMain`; race it with a server Effect when
186
+ * the same process also serves requests. Unlike `Layer.launch`, this observes worker failures.
187
+ */
188
+ const run = Effect.gen(function* () {
189
+ return yield* (yield* NodeDurableHost).run;
190
+ });
191
+ //#endregion
192
+ export { run as a, layer as i, NodeDurableHost as n, NodeAdmission as o, NodeDurableHost_exports as r, makeNodePreparedInputAdmission as s, AdmissionClosed as t };
193
+
194
+ //# sourceMappingURL=NodeDurableHost-BMSajuQn.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodeDurableHost-BMSajuQn.mjs","names":[],"sources":["../src/internal/message-delivery.ts","../src/internal/prepared-admission.ts","../src/NodeDurableHost.ts"],"sourcesContent":["import {\n MessageDeliveryDriver,\n type MessageDeliveryFailure,\n MessageDeliveryStore,\n} from \"@effect-agent/thread/MessageDelivery\";\nimport { Cause, Clock, Effect, Exit, Option } from \"effect\";\n\nconst reportFailure = (cause: Cause.Cause<MessageDeliveryFailure>): Effect.Effect<void> =>\n Cause.hasInterruptsOnly(cause)\n ? Effect.interrupt\n : Effect.logWarning(\"Node message delivery pass failed\").pipe(\n Effect.annotateLogs({\n failureTag: Option.match(Cause.findErrorOption(cause), {\n onNone: () => \"Defect\",\n onSome: (failure) => failure._tag,\n }),\n }),\n );\n\n/** Caller-owned loop. Indexed scans repair absent hints even when both Threads have settled. */\nexport const runNodeMessageDeliveries = Effect.fn(\"NodeMessageDelivery.run\")(function* (\n scanInterval: number,\n) {\n const driver = yield* MessageDeliveryDriver;\n const store = yield* MessageDeliveryStore;\n\n while (true) {\n const pass = yield* driver.runDue().pipe(Effect.exit);\n\n if (Exit.isFailure(pass)) {\n yield* reportFailure(pass.cause);\n yield* Effect.sleep(scanInterval);\n continue;\n }\n\n const deadline = yield* store.nextDeadline().pipe(Effect.exit);\n\n if (Exit.isFailure(deadline)) {\n yield* reportFailure(deadline.cause);\n yield* Effect.sleep(scanInterval);\n continue;\n }\n\n const nowMillis = yield* Clock.currentTimeMillis;\n\n const delay =\n deadline.value === null\n ? scanInterval\n : Math.max(1, Math.min(deadline.value - nowMillis, scanInterval));\n\n yield* Effect.sleep(delay);\n }\n});\n","import { type AgentId } from \"@effect-agent/core/Identifiers\";\nimport { type DurableSubmitAgent } from \"@effect-agent/thread/DurableAgentRuntime\";\nimport { PreparedInputAdmission } from \"@effect-agent/thread/PreparedInputAdmission\";\nimport { PersistedJson } from \"@effect-agent/thread/Records\";\nimport {\n ScheduledInputRetryable,\n ScheduledInputRefused,\n ScheduleStorageError,\n} from \"@effect-agent/thread/Schedule\";\nimport { Context, Effect } from \"effect\";\n\nimport { type NodeDurableHost } from \"../NodeDurableHost.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/** Host-owned admission gate, available while the host's worker pool is being assembled. */\nexport class NodeAdmission extends Context.Service<\n NodeAdmission,\n Pick<NodeDurableHost[\"Service\"], \"submit\" | \"submissionStatus\">\n>()(\"@effect-agent/platform-node/internal/NodeAdmission\") {}\n\n/** Acquire the gated source once; worker callers cannot replace its admission authority. */\nexport const makeNodePreparedInputAdmission = Effect.gen(function* () {\n const host = yield* NodeAdmission;\n\n return PreparedInputAdmission.of({\n submissionStatus: (receipt) =>\n host\n .submissionStatus(receipt)\n .pipe(Effect.mapError(() => ScheduledInputRetryable.make({ reason: \"storage\" }))),\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 ...(envelope.admissionGroup === undefined\n ? {}\n : { admissionGroup: envelope.admissionGroup }),\n ...(envelope.admissionFence === undefined\n ? {}\n : { admissionFence: envelope.admissionFence }),\n ...(envelope.workerAdmission === undefined\n ? {}\n : { workerAdmission: envelope.workerAdmission }),\n ...(envelope.messageAdmission === undefined\n ? {}\n : { messageAdmission: envelope.messageAdmission }),\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 AdmissionPolicyError: (error) =>\n error.reason === \"refused\"\n ? ScheduledInputRefused.make({ code: error.code })\n : ScheduledInputRetryable.make({\n reason: error.reason === \"occupied\" ? \"capacity\" : \"storage\",\n }),\n LedgerError: () => ScheduledInputRetryable.make({ reason: \"storage\" }),\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","import { type ThreadId, type SubmissionId } from \"@effect-agent/core/Identifiers\";\nimport {\n type IntegrityReport,\n type ObligationReport,\n type ObligationThresholds,\n type RecoveryExplanation,\n type RetryCommand,\n} from \"@effect-agent/thread/Admin\";\nimport {\n type AgentRegistration,\n type ResolvedBinding,\n type DurableBindingFailure,\n} from \"@effect-agent/thread/AgentRegistration\";\nimport {\n DurableAgentRuntime,\n type DurableAbortFailure,\n type DurableAwaitFailure,\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 Receipt,\n type RecoveryReport,\n} from \"@effect-agent/thread/DurableAgentRuntime\";\nimport {\n type MessageDeliveryStore,\n MessageDeliveryDriver,\n type MessageDeliveryError,\n} from \"@effect-agent/thread/MessageDelivery\";\nimport { type OperationDenied } from \"@effect-agent/thread/OperationAuthorizer\";\nimport { PreparedInputAdmission } from \"@effect-agent/thread/PreparedInputAdmission\";\nimport { type CanonicalRecordEnvelope } from \"@effect-agent/thread/Records\";\nimport {\n type AbortCommand,\n type AbortIntent,\n type Settlement,\n} from \"@effect-agent/thread/SubmissionLedger\";\nimport {\n type ThreadNotMaterialized,\n type ThreadStoreError,\n} from \"@effect-agent/thread/ThreadStore\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { type Stream, Context, Effect, Fiber, Layer, Ref, Schema } from \"effect\";\n\nimport { runNodeMessageDeliveries } from \"./internal/message-delivery.ts\";\nimport { makeNodePreparedInputAdmission, NodeAdmission } from \"./internal/prepared-admission.ts\";\nimport {\n NodeDurableAgentRuntime,\n NodeDurableAgentRuntimeConfig,\n type NodeDurableAgentRuntimeOptions,\n} from \"./NodeDurableAgentRuntime.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 = Effect.fn(\"NodeDurableHost.make\")(function* (startWorkers: boolean) {\n const runtime = yield* DurableAgentRuntime;\n const config = yield* NodeDurableAgentRuntimeConfig;\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 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 deliveryServices = yield* Effect.context<MessageDeliveryStore>();\n\n const deliveryContext = yield* Layer.build(\n MessageDeliveryDriver.layer({\n batchSize: 100,\n concurrency: Math.min(config.workerConcurrency, 32),\n }).pipe(\n Layer.provide(NodeCrypto.layer),\n Layer.provide(\n Layer.effect(PreparedInputAdmission, makeNodePreparedInputAdmission).pipe(\n Layer.provide(\n Layer.succeed(NodeAdmission, { submit, submissionStatus: runtime.submissionStatus }),\n ),\n ),\n ),\n ),\n );\n\n const runDeliveries = runNodeMessageDeliveries(config.wakeScanInterval).pipe(\n Effect.provide(Context.merge(deliveryServices, deliveryContext)),\n );\n\n const runWorkers = <A, E, R>(worker: Effect.Effect<A, E, R>): Effect.Effect<void, E, R> =>\n Effect.scoped(\n // Either side exiting stops and joins the other; delivery interruption cannot leave\n // an apparently healthy worker pool running without message recovery.\n Effect.raceFirst(\n Effect.forEach(\n Array.from({ length: config.workerConcurrency }, (_, index) => index),\n () => worker,\n { concurrency: \"unbounded\", discard: true },\n ),\n runDeliveries,\n ),\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);\n\n const run = startWorkers\n ? Fiber.join(\n yield* runResolvedWorkers.pipe(\n Effect.onExit(() => Ref.set(admission, false)),\n Effect.forkScoped,\n ),\n )\n : runResolvedWorkers;\n\n // Register after the worker fiber: close admission, interrupt/join workers, drain\n // runtime ownership, then close storage and captured application services.\n yield* Effect.addFinalizer(() => Ref.set(admission, false));\n\n return NodeDurableHost.of({\n startupRecovery,\n admissionOpen: Ref.get(admission),\n submit,\n awaitSettlement: runtime.awaitSettlement,\n submissionStatus: runtime.submissionStatus,\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 run,\n runResolvedWorkers: run,\n });\n});\n\n/**\n * Operational host service. Prefer the module's `layer` and `run` for managed workers.\n * The static constructors on this class retain explicit, manual worker ownership.\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 /** Observe the managed worker pool, preserving its failure. Manual hosts start their pool here. */\n readonly run: Effect.Effect<void, DurableWorkerFailure | DurableBindingFailure>;\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 submissionStatus: DurableAgentRuntime[\"Service\"][\"submissionStatus\"];\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 * same Scope drives pending message admission and settlement observation independently\n * of Submission liveness. 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 * Hosts built with the module-level `layer` instead join their existing pool.\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 ReconcilerError = never,\n ReconcilerRequirements = never,\n >(\n registrations: Entries,\n options: NodeDurableAgentRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements,\n ReconcilerError,\n ReconcilerRequirements\n >,\n ) {\n return NodeDurableHost.layer.pipe(\n Layer.provideMerge(NodeDurableAgentRuntime.layerRegistered(registrations, options)),\n );\n }\n\n /**\n * Host gates over an assembled `NodeDurableAgentRuntime` stack. The runtime Layer owns\n * executable registrations; omission registers no Agents, so resolved work fails closed.\n */\n static readonly layer: Layer.Layer<\n NodeDurableHost,\n DurableWorkerFailure | MessageDeliveryError,\n DurableAgentRuntime | NodeDurableAgentRuntimeConfig | MessageDeliveryStore\n > = Layer.effect(NodeDurableHost)(makeHost(false));\n\n /** The complete DN host: `NodeDurableAgentRuntime.layer(options)` plus the host lifecycle gates. */\n static layerStack<\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n ReconcilerError = never,\n ReconcilerRequirements = never,\n >(\n options: NodeDurableAgentRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements,\n ReconcilerError,\n ReconcilerRequirements\n > & { readonly bindings?: ReadonlyArray<ResolvedBinding> },\n ) {\n const { bindings = [], ...runtimeOptions } = options;\n\n return NodeDurableHost.layer.pipe(\n Layer.provideMerge(NodeDurableAgentRuntime.layerWithBindings(bindings, runtimeOptions)),\n );\n }\n}\n\n/**\n * Acquire a complete Node host and start one bounded, scoped worker pool after recovery.\n * Provide model, tool, instruction, and schema dependencies to this Layer. Reusing the Layer\n * shares the same pool. A worker failure closes admission; observe it with `run` at the process\n * boundary so the application exits and releases the host instead of remaining idle.\n */\nexport const layer = <\n const Entries extends ReadonlyArray<AgentRegistration>,\n ContextError = never,\n ContextRequirements = never,\n AuthorizationError = never,\n AuthorizationRequirements = never,\n ReconcilerError = never,\n ReconcilerRequirements = never,\n>(\n registrations: Entries,\n options: NodeDurableAgentRuntimeOptions<\n ContextError,\n ContextRequirements,\n AuthorizationError,\n AuthorizationRequirements,\n ReconcilerError,\n ReconcilerRequirements\n >,\n) =>\n Layer.effect(NodeDurableHost)(makeHost(true)).pipe(\n Layer.provideMerge(NodeDurableAgentRuntime.layerRegistered(registrations, options)),\n );\n\n/**\n * Supervise the host's existing workers without starting another pool. Use with\n * `Effect.provide(HostLive)` and `NodeRuntime.runMain`; race it with a server Effect when\n * the same process also serves requests. Unlike `Layer.launch`, this observes worker failures.\n */\nexport const run = Effect.gen(function* () {\n const host = yield* NodeDurableHost;\n\n return yield* host.run;\n});\n"],"mappings":"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"}
@@ -0,0 +1,168 @@
1
+ import { t as NodeWakeSchedulerConfig } from "./NodeWakeScheduler-4ZeXYwPu.mjs";
2
+ import { a as NodeDurableAgentRuntimeOptions, c as NodePlatformConfigError, n as NodeDurableAgentRuntimeConfig, o as NodeDurableAgentRuntimeServices } from "./NodeDurableAgentRuntime-CFElFzNF.mjs";
3
+ import { SubmissionId, ThreadId } from "@effect-agent/core/Identifiers";
4
+ import { AgentRegistration, DurableBindingFailure, ResolvedBinding } from "@effect-agent/thread/AgentRegistration";
5
+ import { DurableAbortFailure, DurableAgentRuntime, DurableAwaitFailure, DurableExplainFailure, DurableObligationFailure, DurableObserveOptions, DurableRetryFailure, DurableSubmitAgent, DurableSubmitFailure, DurableSubmitOptions, DurableVerifyFailure, DurableWorkerFailure, Receipt, RecoveryReport } from "@effect-agent/thread/DurableAgentRuntime";
6
+ import { MessageDeliveryError, MessageDeliveryStore } from "@effect-agent/thread/MessageDelivery";
7
+ import { CanonicalRecordEnvelope } from "@effect-agent/thread/Records";
8
+ import { AbortCommand, AbortIntent, Settlement } from "@effect-agent/thread/SubmissionLedger";
9
+ import { ThreadNotMaterialized, ThreadStoreError } from "@effect-agent/thread/ThreadStore";
10
+ import { Context, Effect, Layer, Schema, Stream } from "effect";
11
+ import { IntegrityReport, ObligationReport, ObligationThresholds, RecoveryExplanation, RetryCommand } from "@effect-agent/thread/Admin";
12
+ import { OperationDenied } from "@effect-agent/thread/OperationAuthorizer";
13
+ declare namespace NodeDurableHost_d_exports {
14
+ export { AdmissionClosed, NodeDurableHost, layer, run };
15
+ }
16
+ declare const AdmissionClosed_base: Schema.Class<AdmissionClosed, Schema.TaggedStruct<"AdmissionClosed", {
17
+ readonly message: Schema.String;
18
+ }>, import("effect/Cause").YieldableError>;
19
+ /**
20
+ * Admission is not open on this host: it is shutting down (deployment §6 step 1, DEPLOY-005).
21
+ * Accepted work is unaffected — only NEW admissions are refused.
22
+ */
23
+ declare class AdmissionClosed extends AdmissionClosed_base {}
24
+ declare const NodeDurableHost_base: Context.ServiceClass<NodeDurableHost, "@effect-agent/platform-node/NodeDurableHost", {
25
+ /**
26
+ * The recovery decisions executed (or deferred) by this host's startup reconciliation.
27
+ * Reports with the `unknown` disposition identify lanes blocked on Unknown Outcomes that
28
+ * only the authorized DUR-017 resolution path can release.
29
+ */
30
+ readonly startupRecovery: ReadonlyArray<RecoveryReport>;
31
+ /** Admission-role readiness (deployment §7): true until shutdown begins. */
32
+ readonly admissionOpen: Effect.Effect<boolean>;
33
+ /** Observe the managed worker pool, preserving its failure. Manual hosts start their pool here. */
34
+ readonly run: Effect.Effect<void, DurableWorkerFailure | DurableBindingFailure>;
35
+ /** `DurableAgentRuntime.submit` behind the host admission gate. */
36
+ readonly submit: <InputSchema extends Schema.Top>(agent: DurableSubmitAgent<InputSchema>, input: InputSchema["Type"], options: DurableSubmitOptions) => Effect.Effect<Receipt, AdmissionClosed | DurableSubmitFailure, InputSchema["EncodingServices"]>;
37
+ readonly submissionStatus: DurableAgentRuntime["Service"]["submissionStatus"];
38
+ readonly awaitSettlement: (receipt: Receipt) => Effect.Effect<Settlement, DurableAwaitFailure>;
39
+ readonly observe: (receipt: Receipt, options?: DurableObserveOptions) => Stream.Stream<CanonicalRecordEnvelope, ThreadStoreError | ThreadNotMaterialized | OperationDenied>;
40
+ readonly abort: (command: AbortCommand) => Effect.Effect<AbortIntent, DurableAbortFailure>;
41
+ /** `DurableAgentRuntime.explain` — read-only recovery explanation of one Submission (P7). */
42
+ readonly explain: (submissionId: SubmissionId) => Effect.Effect<RecoveryExplanation, DurableExplainFailure>;
43
+ /** `DurableAgentRuntime.explainThread` — explain every nonterminal lane member. */
44
+ readonly explainThread: (threadId: ThreadId) => Effect.Effect<ReadonlyArray<RecoveryExplanation>, DurableExplainFailure>;
45
+ /** `DurableAgentRuntime.verify` — read-only integrity checks, never a repair (P7). */
46
+ readonly verify: (threadId: ThreadId) => Effect.Effect<IntegrityReport, DurableVerifyFailure>;
47
+ /** `DurableAgentRuntime.retry` — audited single-Submission re-drive with typed refusals. */
48
+ readonly retry: (command: RetryCommand) => Effect.Effect<RecoveryReport, DurableRetryFailure>;
49
+ /** `DurableAgentRuntime.wake` — the documented operator liveness nudge for one lane. */
50
+ readonly wake: (threadId: ThreadId) => Effect.Effect<void, OperationDenied>;
51
+ /** `DurableAgentRuntime.scanObligations` — the scan-based DUR-017/OPS-001 report. */
52
+ readonly scanObligations: (thresholds: ObligationThresholds) => Effect.Effect<ObligationReport, DurableObligationFailure>;
53
+ /**
54
+ * Run `workerConcurrency` copies of the given worker effect (typically
55
+ * `DurableAgentRuntime.runWorker(agent)`) until the caller's Scope interrupts them. The
56
+ * same Scope drives pending message admission and settlement observation independently
57
+ * of Submission liveness. The host never forks daemon fibers.
58
+ */
59
+ readonly runWorkers: <A, E, R>(worker: Effect.Effect<A, E, R>) => Effect.Effect<void, E, R>;
60
+ /**
61
+ * Run `workerConcurrency` copies of `DurableAgentRuntime.runResolvedWorker` over the host's
62
+ * registered Bindings (S2): every claimed head resolves its exact stored Binding before any
63
+ * code runs (SUB-023), so one bounded pool serves parent and attached-child lanes.
64
+ * Hosts built with the module-level `layer` instead join their existing pool.
65
+ */
66
+ readonly runResolvedWorkers: Effect.Effect<void, DurableWorkerFailure | DurableBindingFailure>;
67
+ }>;
68
+ /**
69
+ * Operational host service. Prefer the module's `layer` and `run` for managed workers.
70
+ * The static constructors on this class retain explicit, manual worker ownership.
71
+ *
72
+ * Startup gates run during Layer construction, so the service existing implies readiness:
73
+ * configuration was schema-decoded, the SQLite file passed the exact-version compatibility check,
74
+ * and every nonterminal Submission went through one full recovery pass BEFORE admission opened.
75
+ * `startupRecovery` is the auditable evidence of that reconciliation pass.
76
+ *
77
+ * Shutdown runs in reverse Layer order when the owning Scope closes: `submit` starts refusing
78
+ * with `AdmissionClosed` first, then the runtime Layer's ownership drain releases every claim
79
+ * still held so another host can take over the lanes immediately, then the SQLite resources
80
+ * close. Forced termination at any point stays safe — the durability protocol, not graceful
81
+ * shutdown, provides correctness (DEPLOY-006).
82
+ */
83
+ declare class NodeDurableHost extends NodeDurableHost_base {
84
+ /**
85
+ * Compile typed registrations and acquire the complete host in one Layer Scope.
86
+ * Node supplies Crypto; model, tool, instruction, and schema services remain required.
87
+ * Startup recovery and shutdown gates are unchanged. Workers start only when the caller
88
+ * runs runResolvedWorkers; this constructor never starts a background worker.
89
+ */
90
+ static layerRegistered<const Entries extends ReadonlyArray<AgentRegistration>, ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never, ReconcilerError = never, ReconcilerRequirements = never>(registrations: Entries, options: NodeDurableAgentRuntimeOptions<ContextError, ContextRequirements, AuthorizationError, AuthorizationRequirements, ReconcilerError, ReconcilerRequirements>): Layer.Layer<NodeDurableHost | NodeDurableAgentRuntimeServices, AuthorizationError | ContextError | ReconcilerError | MessageDeliveryError | NodePlatformConfigError | DurableWorkerFailure | import("@effect-agent/storage-sqlite/SqliteThreadStore").SqliteStorageInitializationError, Exclude<AuthorizationRequirements, import("effect/Crypto").Crypto> | Exclude<ContextRequirements, import("effect/Crypto").Crypto> | Exclude<Exclude<Exclude<ReconcilerRequirements, import("effect/Crypto").Crypto | import("effect/unstable/sql/SqlClient").SqlClient | import("@effect/sql-sqlite-node/SqliteClient").SqliteClient | import("@effect-agent/storage-sqlite/SqliteStorageConfig").SqliteStorageConfig | import("@effect-agent/storage-sqlite/SqliteStorageFailpoint").SqliteStorageFailpoint>, NodeDurableAgentRuntimeConfig>, import("@effect-agent/engine/RunOptions").RunContextPreparation | import("@effect-agent/engine/RunOptions").RunToolAuthorization> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<[Entries[number]] extends [never] ? never : (Entries[number] extends (infer T_3) ? T_3 extends Entries[number] ? T_3 extends {
91
+ readonly attemptLayer: (context: import("@effect-agent/thread/AgentRegistration").AgentAttemptContext) => Layer.Layer<infer Provides, never, infer Requires>;
92
+ } ? Requires | Exclude<T_3 extends (infer T_4) ? T_4 extends T_3 ? T_4 extends {
93
+ readonly agent: infer A extends import("@effect-agent/thread/AgentRegistration").ExecutableAgentBinding;
94
+ } ? import("@effect-agent/thread/DurableAgentRuntime").DurableWorkerRequirements<A> : T_4 extends {
95
+ readonly agent: infer D extends import("@effect-agent/core/Agent").Definition<Schema.Top, Schema.Top, unknown, import("effect/unstable/ai/Toolkit").Any, import("@effect-agent/core/Agent").RunDispositionDeclaration<never, Schema.Top> | undefined, unknown> & {
96
+ readonly instructions: import("@effect-agent/core/Agent").InstructionSource<never, unknown, unknown>;
97
+ readonly inputPrompt?: import("@effect-agent/core/Agent").InputPromptSource<never, unknown, unknown> | undefined;
98
+ };
99
+ readonly model: infer M extends import("@effect-agent/thread/AgentRegistration").ExecutableAgentBinding["model"];
100
+ } ? import("@effect-agent/thread/DurableAgentRuntime").DurableWorkerRequirements<{
101
+ readonly definition: D;
102
+ readonly model: M;
103
+ }> : never : never : never, Provides> : T_3 extends {
104
+ readonly agent: infer A extends import("@effect-agent/thread/AgentRegistration").ExecutableAgentBinding;
105
+ } ? import("@effect-agent/thread/DurableAgentRuntime").DurableWorkerRequirements<A> : T_3 extends {
106
+ readonly agent: infer D extends import("@effect-agent/core/Agent").Definition<Schema.Top, Schema.Top, unknown, import("effect/unstable/ai/Toolkit").Any, import("@effect-agent/core/Agent").RunDispositionDeclaration<never, Schema.Top> | undefined, unknown> & {
107
+ readonly instructions: import("@effect-agent/core/Agent").InstructionSource<never, unknown, unknown>;
108
+ readonly inputPrompt?: import("@effect-agent/core/Agent").InputPromptSource<never, unknown, unknown> | undefined;
109
+ };
110
+ readonly model: infer M extends import("@effect-agent/thread/AgentRegistration").ExecutableAgentBinding["model"];
111
+ } ? import("@effect-agent/thread/DurableAgentRuntime").DurableWorkerRequirements<{
112
+ readonly definition: D;
113
+ readonly model: M;
114
+ }> : never : never : never) | (Entries[number] extends (infer T_5) ? T_5 extends Entries[number] ? T_5 extends {
115
+ readonly reporting: infer Reports extends ReadonlyArray<import("@effect-agent/engine/SubagentHost").WorkerReporting<unknown, unknown>>;
116
+ } ? [Reports[number]] extends [never] ? never : Reports[number] extends import("@effect-agent/engine/SubagentHost").WorkerReporting<unknown, infer R> ? Exclude<R, import("effect/Scope").Scope> : never : never : never : never), import("effect/Scope").Scope>, import("@effect-agent/thread/DurableAgentRuntime").DurableRuntimeConfig | MessageDeliveryStore | import("@effect-agent/thread/Schedule").ScheduleStore | import("@effect-agent/thread/SubmissionLedger").SubmissionLedger | import("@effect-agent/thread/ThreadStore").ThreadStore | import("@effect-agent/thread/WakeScheduler").WakeScheduler>, import("@effect-agent/thread/DurableFailpoint").DurableRuntimeFailpoint | NodeWakeSchedulerConfig | import("@effect-agent/thread/ToolReconciler").ToolReconciler>, import("effect/Crypto").Crypto | import("effect/unstable/sql/SqlClient").SqlClient | import("@effect/sql-sqlite-node/SqliteClient").SqliteClient | import("@effect-agent/storage-sqlite/SqliteStorageConfig").SqliteStorageConfig | import("@effect-agent/storage-sqlite/SqliteStorageFailpoint").SqliteStorageFailpoint>, NodeDurableAgentRuntimeConfig>, import("@effect-agent/engine/RunOptions").RunContextPreparation | import("@effect-agent/engine/RunOptions").RunToolAuthorization>>;
117
+ /**
118
+ * Host gates over an assembled `NodeDurableAgentRuntime` stack. The runtime Layer owns
119
+ * executable registrations; omission registers no Agents, so resolved work fails closed.
120
+ */
121
+ static readonly layer: Layer.Layer<NodeDurableHost, DurableWorkerFailure | MessageDeliveryError, DurableAgentRuntime | NodeDurableAgentRuntimeConfig | MessageDeliveryStore>;
122
+ /** The complete DN host: `NodeDurableAgentRuntime.layer(options)` plus the host lifecycle gates. */
123
+ static layerStack<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never, ReconcilerError = never, ReconcilerRequirements = never>(options: NodeDurableAgentRuntimeOptions<ContextError, ContextRequirements, AuthorizationError, AuthorizationRequirements, ReconcilerError, ReconcilerRequirements> & {
124
+ readonly bindings?: ReadonlyArray<ResolvedBinding>;
125
+ }): Layer.Layer<NodeDurableHost | NodeDurableAgentRuntimeServices, AuthorizationError | ContextError | ReconcilerError | MessageDeliveryError | NodePlatformConfigError | DurableWorkerFailure | import("@effect-agent/storage-sqlite/SqliteThreadStore").SqliteStorageInitializationError, Exclude<AuthorizationRequirements, import("effect/Crypto").Crypto> | Exclude<ContextRequirements, import("effect/Crypto").Crypto> | Exclude<Exclude<Exclude<ReconcilerRequirements, import("effect/Crypto").Crypto | import("effect/unstable/sql/SqlClient").SqlClient | import("@effect/sql-sqlite-node/SqliteClient").SqliteClient | import("@effect-agent/storage-sqlite/SqliteStorageConfig").SqliteStorageConfig | import("@effect-agent/storage-sqlite/SqliteStorageFailpoint").SqliteStorageFailpoint>, NodeDurableAgentRuntimeConfig>, import("@effect-agent/engine/RunOptions").RunContextPreparation | import("@effect-agent/engine/RunOptions").RunToolAuthorization>>;
126
+ }
127
+ /**
128
+ * Acquire a complete Node host and start one bounded, scoped worker pool after recovery.
129
+ * Provide model, tool, instruction, and schema dependencies to this Layer. Reusing the Layer
130
+ * shares the same pool. A worker failure closes admission; observe it with `run` at the process
131
+ * boundary so the application exits and releases the host instead of remaining idle.
132
+ */
133
+ declare const layer: <const Entries extends ReadonlyArray<AgentRegistration>, ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never, ReconcilerError = never, ReconcilerRequirements = never>(registrations: Entries, options: NodeDurableAgentRuntimeOptions<ContextError, ContextRequirements, AuthorizationError, AuthorizationRequirements, ReconcilerError, ReconcilerRequirements>) => Layer.Layer<NodeDurableHost | NodeDurableAgentRuntimeServices, AuthorizationError | ContextError | ReconcilerError | MessageDeliveryError | NodePlatformConfigError | DurableWorkerFailure | import("@effect-agent/storage-sqlite/SqliteThreadStore").SqliteStorageInitializationError, Exclude<AuthorizationRequirements, import("effect/Crypto").Crypto> | Exclude<ContextRequirements, import("effect/Crypto").Crypto> | Exclude<Exclude<Exclude<ReconcilerRequirements, import("effect/Crypto").Crypto | import("effect/unstable/sql/SqlClient").SqlClient | import("@effect/sql-sqlite-node/SqliteClient").SqliteClient | import("@effect-agent/storage-sqlite/SqliteStorageConfig").SqliteStorageConfig | import("@effect-agent/storage-sqlite/SqliteStorageFailpoint").SqliteStorageFailpoint>, NodeDurableAgentRuntimeConfig>, import("@effect-agent/engine/RunOptions").RunContextPreparation | import("@effect-agent/engine/RunOptions").RunToolAuthorization> | Exclude<Exclude<Exclude<Exclude<Exclude<Exclude<[Entries[number]] extends [never] ? never : (Entries[number] extends (infer T_3) ? T_3 extends Entries[number] ? T_3 extends {
134
+ readonly attemptLayer: (context: import("@effect-agent/thread/AgentRegistration").AgentAttemptContext) => Layer.Layer<infer Provides, never, infer Requires>;
135
+ } ? Requires | Exclude<T_3 extends (infer T_4) ? T_4 extends T_3 ? T_4 extends {
136
+ readonly agent: infer A extends import("@effect-agent/thread/AgentRegistration").ExecutableAgentBinding;
137
+ } ? import("@effect-agent/thread/DurableAgentRuntime").DurableWorkerRequirements<A> : T_4 extends {
138
+ readonly agent: infer D extends import("@effect-agent/core/Agent").Definition<Schema.Top, Schema.Top, unknown, import("effect/unstable/ai/Toolkit").Any, import("@effect-agent/core/Agent").RunDispositionDeclaration<never, Schema.Top> | undefined, unknown> & {
139
+ readonly instructions: import("@effect-agent/core/Agent").InstructionSource<never, unknown, unknown>;
140
+ readonly inputPrompt?: import("@effect-agent/core/Agent").InputPromptSource<never, unknown, unknown> | undefined;
141
+ };
142
+ readonly model: infer M extends import("@effect-agent/thread/AgentRegistration").ExecutableAgentBinding["model"];
143
+ } ? import("@effect-agent/thread/DurableAgentRuntime").DurableWorkerRequirements<{
144
+ readonly definition: D;
145
+ readonly model: M;
146
+ }> : never : never : never, Provides> : T_3 extends {
147
+ readonly agent: infer A extends import("@effect-agent/thread/AgentRegistration").ExecutableAgentBinding;
148
+ } ? import("@effect-agent/thread/DurableAgentRuntime").DurableWorkerRequirements<A> : T_3 extends {
149
+ readonly agent: infer D extends import("@effect-agent/core/Agent").Definition<Schema.Top, Schema.Top, unknown, import("effect/unstable/ai/Toolkit").Any, import("@effect-agent/core/Agent").RunDispositionDeclaration<never, Schema.Top> | undefined, unknown> & {
150
+ readonly instructions: import("@effect-agent/core/Agent").InstructionSource<never, unknown, unknown>;
151
+ readonly inputPrompt?: import("@effect-agent/core/Agent").InputPromptSource<never, unknown, unknown> | undefined;
152
+ };
153
+ readonly model: infer M extends import("@effect-agent/thread/AgentRegistration").ExecutableAgentBinding["model"];
154
+ } ? import("@effect-agent/thread/DurableAgentRuntime").DurableWorkerRequirements<{
155
+ readonly definition: D;
156
+ readonly model: M;
157
+ }> : never : never : never) | (Entries[number] extends (infer T_5) ? T_5 extends Entries[number] ? T_5 extends {
158
+ readonly reporting: infer Reports extends ReadonlyArray<import("@effect-agent/engine/SubagentHost").WorkerReporting<unknown, unknown>>;
159
+ } ? [Reports[number]] extends [never] ? never : Reports[number] extends import("@effect-agent/engine/SubagentHost").WorkerReporting<unknown, infer R> ? Exclude<R, import("effect/Scope").Scope> : never : never : never : never), import("effect/Scope").Scope>, import("@effect-agent/thread/DurableAgentRuntime").DurableRuntimeConfig | MessageDeliveryStore | import("@effect-agent/thread/Schedule").ScheduleStore | import("@effect-agent/thread/SubmissionLedger").SubmissionLedger | import("@effect-agent/thread/ThreadStore").ThreadStore | import("@effect-agent/thread/WakeScheduler").WakeScheduler>, import("@effect-agent/thread/DurableFailpoint").DurableRuntimeFailpoint | NodeWakeSchedulerConfig | import("@effect-agent/thread/ToolReconciler").ToolReconciler>, import("effect/Crypto").Crypto | import("effect/unstable/sql/SqlClient").SqlClient | import("@effect/sql-sqlite-node/SqliteClient").SqliteClient | import("@effect-agent/storage-sqlite/SqliteStorageConfig").SqliteStorageConfig | import("@effect-agent/storage-sqlite/SqliteStorageFailpoint").SqliteStorageFailpoint>, NodeDurableAgentRuntimeConfig>, import("@effect-agent/engine/RunOptions").RunContextPreparation | import("@effect-agent/engine/RunOptions").RunToolAuthorization>>;
160
+ /**
161
+ * Supervise the host's existing workers without starting another pool. Use with
162
+ * `Effect.provide(HostLive)` and `NodeRuntime.runMain`; race it with a server Effect when
163
+ * the same process also serves requests. Unlike `Layer.launch`, this observes worker failures.
164
+ */
165
+ declare const run: Effect.Effect<void, DurableBindingFailure | DurableWorkerFailure, NodeDurableHost>;
166
+ //#endregion
167
+ export { AdmissionClosed, NodeDurableHost, layer, run, NodeDurableHost_d_exports as t };
168
+ //# sourceMappingURL=NodeDurableHost.d.mts.map
@@ -0,0 +1,2 @@
1
+ import { a as run, i as layer, n as NodeDurableHost, t as AdmissionClosed } from "./NodeDurableHost-BMSajuQn.mjs";
2
+ export { AdmissionClosed, NodeDurableHost, layer, run };
@@ -0,0 +1,22 @@
1
+ import { NodeDurableHost } from "./NodeDurableHost.mjs";
2
+ import { ScheduleAuthorizer, ScheduleStore, ScheduleValidationError, ScheduleWake, SchedulingLimits } from "@effect-agent/thread/Schedule";
3
+ import { Layer } from "effect";
4
+ import { Scheduling } from "@effect-agent/thread/Scheduling";
5
+ declare namespace NodeScheduling_d_exports {
6
+ export { NodeScheduling, NodeSchedulingOptions, nodeScheduleWakeLayer };
7
+ }
8
+ /** One bounded hint slot for the single supported Node scheduler. Indexed polling repairs loss. */
9
+ declare const nodeScheduleWakeLayer: Layer.Layer<ScheduleWake>;
10
+ interface NodeSchedulingOptions {
11
+ readonly limits?: SchedulingLimits | undefined;
12
+ }
13
+ /**
14
+ * Optional Scope-owned Node scheduling service and driver. The caller must provide the existing
15
+ * host, its SQLite ScheduleStore, and an explicit ScheduleAuthorizer.
16
+ */
17
+ declare class NodeScheduling {
18
+ static layer(options?: NodeSchedulingOptions): Layer.Layer<Scheduling, ScheduleValidationError, NodeDurableHost | ScheduleStore | ScheduleAuthorizer>;
19
+ }
20
+ //#endregion
21
+ export { NodeScheduling, NodeSchedulingOptions, nodeScheduleWakeLayer, NodeScheduling_d_exports as t };
22
+ //# sourceMappingURL=NodeScheduling.d.mts.map
@@ -0,0 +1,59 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { nodeScheduledInputAdmissionLayer } from "./NodeSubscriptions.mjs";
3
+ import { ScheduleStorageError, ScheduleStore, ScheduleWake, defaultSchedulingLimits } from "@effect-agent/thread/Schedule";
4
+ import { NodeCrypto } from "@effect/platform-node";
5
+ import { Cause, Duration, Effect, Layer, Option, PubSub, Result } from "effect";
6
+ import { ScheduleDriver, Scheduling } from "@effect-agent/thread/Scheduling";
7
+ //#region src/NodeScheduling.ts
8
+ var NodeScheduling_exports = /* @__PURE__ */ __exportAll({
9
+ NodeScheduling: () => NodeScheduling,
10
+ nodeScheduleWakeLayer: () => nodeScheduleWakeLayer
11
+ });
12
+ /** One bounded hint slot for the single supported Node scheduler. Indexed polling repairs loss. */
13
+ const nodeScheduleWakeLayer = Layer.effect(ScheduleWake, Effect.gen(function* () {
14
+ const hints = yield* PubSub.sliding(1);
15
+ const subscription = yield* PubSub.subscribe(hints);
16
+ yield* Effect.addFinalizer(() => PubSub.shutdown(hints));
17
+ return ScheduleWake.of({
18
+ notify: PubSub.publish(hints, void 0).pipe(Effect.asVoid),
19
+ await: PubSub.take(subscription)
20
+ });
21
+ }));
22
+ const reportPassFailure = (cause) => Cause.hasInterruptsOnly(cause) ? Effect.interrupt : Effect.logWarning("Node scheduling pass failed").pipe(Effect.annotateLogs({ failureTag: Option.match(Cause.findErrorOption(cause), {
23
+ onNone: () => "Defect",
24
+ onSome: (error) => error._tag
25
+ }) }), Effect.as(false));
26
+ const nodeSchedulingDriverLayer = (limits) => Layer.effectDiscard(Effect.gen(function* () {
27
+ const scheduling = yield* ScheduleDriver;
28
+ const store = yield* ScheduleStore;
29
+ const wake = yield* ScheduleWake;
30
+ const run = Effect.gen(function* () {
31
+ while (true) {
32
+ const passSucceeded = yield* scheduling.runDue().pipe(Effect.map((pass) => pass.failed === 0), Effect.catchCause(reportPassFailure));
33
+ const deadlineResult = passSucceeded ? yield* store.nextDeadline().pipe(Effect.result) : Result.fail(ScheduleStorageError.make({
34
+ operation: "driver pass",
35
+ reason: "unavailable"
36
+ }));
37
+ if (Result.isFailure(deadlineResult) && passSucceeded) yield* Effect.logWarning("Node scheduling deadline query failed");
38
+ const nowMillis = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
39
+ const deadlineDelay = Result.isSuccess(deadlineResult) && deadlineResult.success !== null ? Math.max(0, deadlineResult.success - nowMillis) : limits.recoveryPollMillis;
40
+ const delay = Math.min(deadlineDelay, limits.recoveryPollMillis);
41
+ yield* Effect.raceFirst(wake.await, Effect.sleep(Duration.millis(delay)));
42
+ }
43
+ });
44
+ yield* Effect.forkScoped(run);
45
+ }));
46
+ /**
47
+ * Optional Scope-owned Node scheduling service and driver. The caller must provide the existing
48
+ * host, its SQLite ScheduleStore, and an explicit ScheduleAuthorizer.
49
+ */
50
+ var NodeScheduling = class {
51
+ static layer(options = {}) {
52
+ const limits = options.limits ?? defaultSchedulingLimits;
53
+ return nodeSchedulingDriverLayer(limits).pipe(Layer.provide(ScheduleDriver.layer(limits)), Layer.merge(Scheduling.layer(limits))).pipe(Layer.provide(Layer.mergeAll(nodeScheduledInputAdmissionLayer, nodeScheduleWakeLayer, NodeCrypto.layer)));
54
+ }
55
+ };
56
+ //#endregion
57
+ export { NodeScheduling, nodeScheduleWakeLayer, NodeScheduling_exports as t };
58
+
59
+ //# sourceMappingURL=NodeScheduling.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodeScheduling.mjs","names":[],"sources":["../src/NodeScheduling.ts"],"sourcesContent":["import {\n type ScheduleAuthorizer,\n type SchedulingLimits,\n ScheduleStorageError,\n ScheduleStore,\n type ScheduleValidationError,\n ScheduleWake,\n defaultSchedulingLimits,\n} from \"@effect-agent/thread/Schedule\";\nimport {\n type ScheduleProcessFailure,\n Scheduling,\n ScheduleDriver,\n} from \"@effect-agent/thread/Scheduling\";\nimport { NodeCrypto } from \"@effect/platform-node\";\nimport { Cause, Duration, Effect, Layer, Option, PubSub, Result } from \"effect\";\n\nimport type { NodeDurableHost } from \"./NodeDurableHost.ts\";\nimport { nodeScheduledInputAdmissionLayer } from \"./NodeSubscriptions.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":";;;;;;;;;;;;AAqBA,MAAa,wBAAmD,MAAM,OACpE,cACA,OAAO,IAAI,aAAa;CACtB,MAAM,QAAQ,OAAO,OAAO,QAAc,CAAC;CAC3C,MAAM,eAAe,OAAO,OAAO,UAAU,KAAK;CAElD,OAAO,OAAO,mBAAmB,OAAO,SAAS,KAAK,CAAC;CAEvD,OAAO,aAAa,GAAG;EACrB,QAAQ,OAAO,QAAQ,OAAO,KAAA,CAAS,CAAC,CAAC,KAAK,OAAO,MAAM;EAC3D,OAAO,OAAO,KAAK,YAAY;CACjC,CAAC;AACH,CAAC,CACH;AAEA,MAAM,qBAAqB,UACzB,MAAM,kBAAkB,KAAK,IACzB,OAAO,YACP,OAAO,WAAW,6BAA6B,CAAC,CAAC,KAC/C,OAAO,aAAa,EAClB,YAAY,OAAO,MAAM,MAAM,gBAAgB,KAAK,GAAG;CACrD,cAAc;CACd,SAAS,UAAU,MAAM;AAC3B,CAAC,EACH,CAAC,GACD,OAAO,GAAG,KAAK,CACjB;AAEN,MAAM,6BACJ,WAEA,MAAM,cACJ,OAAO,IAAI,aAAa;CACtB,MAAM,aAAa,OAAO;CAC1B,MAAM,QAAQ,OAAO;CACrB,MAAM,OAAO,OAAO;CAEpB,MAAM,MAAM,OAAO,IAAI,aAAa;EAClC,OAAO,MAAM;GACX,MAAM,gBAAgB,OAAO,WAAW,OAAO,CAAC,CAAC,KAC/C,OAAO,KAAK,SAAS,KAAK,WAAW,CAAC,GACtC,OAAO,WAAW,iBAAiB,CACrC;GAEA,MAAM,iBAAiB,gBACnB,OAAO,MAAM,aAAa,CAAC,CAAC,KAAK,OAAO,MAAM,IAC9C,OAAO,KACL,qBAAqB,KAAK;IAAE,WAAW;IAAe,QAAQ;GAAc,CAAC,CAC/E;GAEJ,IAAI,OAAO,UAAU,cAAc,KAAK,eACtC,OAAO,OAAO,WAAW,uCAAuC;GAElE,MAAM,YAAY,OAAO,OAAO,WAAW,UAAU,MAAM,iBAAiB;GAE5E,MAAM,gBACJ,OAAO,UAAU,cAAc,KAAK,eAAe,YAAY,OAC3D,KAAK,IAAI,GAAG,eAAe,UAAU,SAAS,IAC9C,OAAO;GAEb,MAAM,QAAQ,KAAK,IAAI,eAAe,OAAO,kBAAkB;GAE/D,OAAO,OAAO,UAAU,KAAK,OAAO,OAAO,MAAM,SAAS,OAAO,KAAK,CAAC,CAAC;EAC1E;CACF,CAAC;CAED,OAAO,OAAO,WAAW,GAAG;AAC9B,CAAC,CACH;;;;;AAUF,IAAa,iBAAb,MAA4B;CAC1B,OAAO,MACL,UAAiC,CAAC,GAKlC;EACA,MAAM,SAAS,QAAQ,UAAU;EAOjC,OAL6B,0BAA0B,MAAM,CAAC,CAAC,KAC7D,MAAM,QAAQ,eAAe,MAAM,MAAM,CAAC,GAC1C,MAAM,MAAM,WAAW,MAAM,MAAM,CAAC,CAGZ,CAAC,CAAC,KAC1B,MAAM,QACJ,MAAM,SAAS,kCAAkC,uBAAuB,WAAW,KAAK,CAC1F,CACF;CACF;AACF"}
@@ -0,0 +1,27 @@
1
+ import { NodeDurableHost } from "./NodeDurableHost.mjs";
2
+ import { ScheduledInputAdmission } from "@effect-agent/thread/Schedule";
3
+ import { Layer } from "effect";
4
+ import { PreparedInputAdmission } from "@effect-agent/thread/PreparedInputAdmission";
5
+ import { EventSources } from "@effect-agent/thread/EventSource";
6
+ import { SubscriptionAuthorizer, SubscriptionError, SubscriptionLimits, SubscriptionStore } from "@effect-agent/thread/Subscription";
7
+ import { SubscriptionInputBindings } from "@effect-agent/thread/SubscriptionInput";
8
+ import { SubscriptionIntake, Subscriptions } from "@effect-agent/thread/Subscriptions";
9
+ declare namespace NodeSubscriptions_d_exports {
10
+ export { NodeSubscriptions, NodeSubscriptionsOptions, nodePreparedInputAdmissionLayer, nodeScheduledInputAdmissionLayer };
11
+ }
12
+ /** Ordinary prepared admission through the Scope-owned Node host gate. */
13
+ declare const nodePreparedInputAdmissionLayer: Layer.Layer<PreparedInputAdmission, never, NodeDurableHost>;
14
+ declare const nodeScheduledInputAdmissionLayer: Layer.Layer<ScheduledInputAdmission, never, NodeDurableHost>;
15
+ interface NodeSubscriptionsOptions {
16
+ readonly limits?: SubscriptionLimits | undefined;
17
+ }
18
+ /**
19
+ * One Scope-owned subscription partition in the sole process owning its SQLite database.
20
+ * Indexed polling repairs restart and lost wake state; closing the Scope interrupts the driver.
21
+ */
22
+ declare class NodeSubscriptions {
23
+ static layer(options?: NodeSubscriptionsOptions): Layer.Layer<Subscriptions | SubscriptionIntake, SubscriptionError, NodeDurableHost | SubscriptionStore | SubscriptionAuthorizer | EventSources | SubscriptionInputBindings>;
24
+ }
25
+ //#endregion
26
+ export { NodeSubscriptions, NodeSubscriptionsOptions, nodePreparedInputAdmissionLayer, nodeScheduledInputAdmissionLayer, NodeSubscriptions_d_exports as t };
27
+ //# sourceMappingURL=NodeSubscriptions.d.mts.map