@effect-agent/platform-node 0.1.0-beta.38 → 0.1.0-beta.40
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 +56 -35
- package/dist/index.mjs +96 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -9
- package/src/host.ts +133 -84
- package/src/index.ts +1 -0
- package/src/layers.ts +82 -45
- package/src/scheduling.ts +3 -57
- package/src/subscriptions.ts +195 -0
- package/src/wake-scheduler.ts +15 -14
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { AbortCommand, AbortIntent,
|
|
2
|
-
import { Context, Duration, Effect, Layer, Schema, Stream } from "effect";
|
|
3
|
-
import { RunCostEstimator, RunToolFailureObserver } from "@effect-agent/engine";
|
|
1
|
+
import { AbortCommand, AbortIntent, AgentRegistration, CanonicalRecordEnvelope, DurableAbortFailure, DurableAgentRuntime, DurableAwaitFailure, DurableBindingFailure, DurableExplainFailure, DurableObligationFailure, DurableObserveOptions, DurableRetryFailure, DurableRuntimeConfig, DurableRuntimeFailpointHandler, DurableSubmitAgent, DurableSubmitFailure, DurableSubmitOptions, DurableVerifyFailure, DurableWorkerFailure, EventSources, IntegrityReport, ObligationReport, ObligationThresholds, OperationDenied, PreparedInputAdmission, Receipt, RecoveryExplanation, RecoveryReport, ResolvedBinding, RetryCommand, ScheduleAuthorizer, ScheduleStore, ScheduleValidationError, ScheduleWake, ScheduledInputAdmission, Scheduling, SchedulingLimits, Settlement, SubmissionLedger, SubscriptionAuthorizer, SubscriptionError, SubscriptionInputBindings, SubscriptionIntake, SubscriptionLimits, SubscriptionStore, Subscriptions, ThreadNotMaterialized, ThreadStore, ThreadStoreError, ToolReconciler, WakeScheduler } from "@effect-agent/thread";
|
|
2
|
+
import { Context, Crypto, Duration, Effect, Layer, Schema, Stream } from "effect";
|
|
3
|
+
import { RunContextPreparation, RunCostEstimator, RunToolAuthorization, RunToolFailureObserver } from "@effect-agent/engine";
|
|
4
4
|
import { SqliteStorageFailpointHandler, SqliteStorageInitializationError } from "@effect-agent/storage-sqlite";
|
|
5
|
-
import {
|
|
5
|
+
import { SubmissionId, ThreadId } from "@effect-agent/core";
|
|
6
6
|
//#region src/layers.d.ts
|
|
7
7
|
declare const NodePlatformConfigError_base: Schema.Class<NodePlatformConfigError, Schema.TaggedStruct<"NodePlatformConfigError", {
|
|
8
8
|
readonly message: Schema.String;
|
|
@@ -11,10 +11,10 @@ declare const NodePlatformConfigError_base: Schema.Class<NodePlatformConfigError
|
|
|
11
11
|
/** The supplied Node durable runtime configuration failed schema validation (DEPLOY-003). */
|
|
12
12
|
declare class NodePlatformConfigError extends NodePlatformConfigError_base {}
|
|
13
13
|
declare const NodeDurableRuntimeConfigValue_base: Schema.Class<NodeDurableRuntimeConfigValue, Schema.Struct<{
|
|
14
|
-
/** SQLite database file backing BOTH the
|
|
14
|
+
/** SQLite database file backing BOTH the Thread Log and the Submission Ledger. */
|
|
15
15
|
readonly filename: Schema.NonEmptyString;
|
|
16
|
-
readonly deploymentId: Schema.brand<Schema.NonEmptyString, "@effect-agent/
|
|
17
|
-
readonly producerId: Schema.brand<Schema.NonEmptyString, "@effect-agent/
|
|
16
|
+
readonly deploymentId: Schema.brand<Schema.NonEmptyString, "@effect-agent/thread/DeploymentId">;
|
|
17
|
+
readonly producerId: Schema.brand<Schema.NonEmptyString, "@effect-agent/thread/ProducerId">;
|
|
18
18
|
/** Submission ownership lease duration (D5); liveness hint only, epochs stay authoritative. */
|
|
19
19
|
readonly ownershipLeaseDuration: Schema.Int;
|
|
20
20
|
/** Finite bound on concurrent worker loops per host (rule 10). */
|
|
@@ -48,7 +48,7 @@ declare class NodeDurableRuntimeConfig extends NodeDurableRuntimeConfig_base {}
|
|
|
48
48
|
* to the documented production values; everything is schema-decoded into
|
|
49
49
|
* `NodeDurableRuntimeConfigValue` before any resource opens (deployment §5 gate 1).
|
|
50
50
|
*/
|
|
51
|
-
interface NodeDurableRuntimeOptions {
|
|
51
|
+
interface NodeDurableRuntimeOptions<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never> {
|
|
52
52
|
readonly filename: string;
|
|
53
53
|
readonly deploymentId: string;
|
|
54
54
|
readonly producerId: string;
|
|
@@ -85,20 +85,19 @@ interface NodeDurableRuntimeOptions {
|
|
|
85
85
|
* DUR-017 resolution path.
|
|
86
86
|
*/
|
|
87
87
|
readonly toolReconciler?: Layer.Layer<ToolReconciler> | undefined;
|
|
88
|
+
/** Host prompt preparation/compaction, acquired once with the runtime; default pass-through. */
|
|
89
|
+
readonly runContext?: Layer.Layer<RunContextPreparation, ContextError, ContextRequirements | Crypto.Crypto> | undefined;
|
|
88
90
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* parent and child lanes from one pool with exact-digest resolution (SUB-023). Defaults to
|
|
93
|
-
* the empty registration: every resolved claim then fails closed (`BindingUnavailable` for a
|
|
94
|
-
* root, the framework `ChildCompatibilityFailure` Settlement for a parent-linked child).
|
|
91
|
+
* Independent action-time Tool authority, acquired once with the runtime; default allow-all.
|
|
92
|
+
* Construction errors and application dependencies remain in the assembled Layer's E and R.
|
|
93
|
+
* The platform supplies Crypto to both extension Layers.
|
|
95
94
|
*/
|
|
96
|
-
readonly
|
|
95
|
+
readonly toolAuthorization?: Layer.Layer<RunToolAuthorization, AuthorizationError, AuthorizationRequirements | Crypto.Crypto> | undefined;
|
|
97
96
|
}
|
|
98
|
-
/**
|
|
97
|
+
/** Built-in construction failures. `layer` also preserves supplied service Layers' errors. */
|
|
99
98
|
type NodeDurableRuntimeInitializationError = NodePlatformConfigError | SqliteStorageInitializationError;
|
|
100
99
|
/** The services `NodeDurableRuntime.layer` provides. */
|
|
101
|
-
type NodeDurableRuntimeServices = DurableAgentRuntime | SubmissionLedger |
|
|
100
|
+
type NodeDurableRuntimeServices = DurableAgentRuntime | SubmissionLedger | ThreadStore | ScheduleStore | WakeScheduler | DurableRuntimeConfig | NodeDurableRuntimeConfig;
|
|
102
101
|
/**
|
|
103
102
|
* Shutdown-drain decorator for a `SubmissionLedger` (deployment §6 step 6): every ownership
|
|
104
103
|
* period granted through this Layer is tracked — claims start tracking, renewals follow token
|
|
@@ -111,7 +110,7 @@ declare const ownershipDrainLayer: Layer.Layer<SubmissionLedger, never, Submissi
|
|
|
111
110
|
/**
|
|
112
111
|
* The DN Layer assembly (deployment §12: a Layer-assembly library, not an app entrypoint).
|
|
113
112
|
* `layer(options)` decodes the configuration, opens ONE SQLite database serving both the
|
|
114
|
-
*
|
|
113
|
+
* Thread Log and the Submission Ledger (so claims fence the same producer epochs), wires
|
|
115
114
|
* the Node wake scheduler with its ledger-scan fallback, wraps the ledger with the shutdown
|
|
116
115
|
* ownership drain, defaults the Tool reconciliation policy to the fail-closed
|
|
117
116
|
* `ToolReconciler.uncertain` (override via `options.toolReconciler`), and provides a ready
|
|
@@ -121,9 +120,9 @@ declare const ownershipDrainLayer: Layer.Layer<SubmissionLedger, never, Submissi
|
|
|
121
120
|
*/
|
|
122
121
|
declare class NodeDurableRuntime {
|
|
123
122
|
/** Validated configuration Layer; fails typed when the supplied options are out of bounds. */
|
|
124
|
-
static configLayer(options: NodeDurableRuntimeOptions): Layer.Layer<NodeDurableRuntimeConfig, NodePlatformConfigError>;
|
|
123
|
+
static configLayer<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never>(options: NodeDurableRuntimeOptions<ContextError, ContextRequirements, AuthorizationError, AuthorizationRequirements>): Layer.Layer<NodeDurableRuntimeConfig, NodePlatformConfigError>;
|
|
125
124
|
/** The full DN runtime stack over one SQLite file. */
|
|
126
|
-
static layer(options: NodeDurableRuntimeOptions): Layer.Layer<NodeDurableRuntimeServices, NodeDurableRuntimeInitializationError
|
|
125
|
+
static layer<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never>(options: NodeDurableRuntimeOptions<ContextError, ContextRequirements, AuthorizationError, AuthorizationRequirements>): Layer.Layer<NodeDurableRuntimeServices, NodeDurableRuntimeInitializationError | ContextError | AuthorizationError, Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>>;
|
|
127
126
|
}
|
|
128
127
|
//#endregion
|
|
129
128
|
//#region src/host.d.ts
|
|
@@ -147,18 +146,18 @@ declare const NodeDurableHost_base: Context.ServiceClass<NodeDurableHost, "@effe
|
|
|
147
146
|
/** `DurableAgentRuntime.submit` behind the host admission gate. */
|
|
148
147
|
readonly submit: <InputSchema extends Schema.Top>(agent: DurableSubmitAgent<InputSchema>, input: InputSchema["Type"], options: DurableSubmitOptions) => Effect.Effect<Receipt, AdmissionClosed | DurableSubmitFailure, InputSchema["EncodingServices"]>;
|
|
149
148
|
readonly awaitSettlement: (receipt: Receipt) => Effect.Effect<Settlement, DurableAwaitFailure>;
|
|
150
|
-
readonly observe: (receipt: Receipt, options?: DurableObserveOptions) => Stream.Stream<CanonicalRecordEnvelope,
|
|
149
|
+
readonly observe: (receipt: Receipt, options?: DurableObserveOptions) => Stream.Stream<CanonicalRecordEnvelope, ThreadStoreError | ThreadNotMaterialized | OperationDenied>;
|
|
151
150
|
readonly abort: (command: AbortCommand) => Effect.Effect<AbortIntent, DurableAbortFailure>;
|
|
152
151
|
/** `DurableAgentRuntime.explain` — read-only recovery explanation of one Submission (P7). */
|
|
153
152
|
readonly explain: (submissionId: SubmissionId) => Effect.Effect<RecoveryExplanation, DurableExplainFailure>;
|
|
154
|
-
/** `DurableAgentRuntime.
|
|
155
|
-
readonly
|
|
153
|
+
/** `DurableAgentRuntime.explainThread` — explain every nonterminal lane member. */
|
|
154
|
+
readonly explainThread: (threadId: ThreadId) => Effect.Effect<ReadonlyArray<RecoveryExplanation>, DurableExplainFailure>;
|
|
156
155
|
/** `DurableAgentRuntime.verify` — read-only integrity checks, never a repair (P7). */
|
|
157
|
-
readonly verify: (
|
|
156
|
+
readonly verify: (threadId: ThreadId) => Effect.Effect<IntegrityReport, DurableVerifyFailure>;
|
|
158
157
|
/** `DurableAgentRuntime.retry` — audited single-Submission re-drive with typed refusals. */
|
|
159
158
|
readonly retry: (command: RetryCommand) => Effect.Effect<RecoveryReport, DurableRetryFailure>;
|
|
160
159
|
/** `DurableAgentRuntime.wake` — the documented operator liveness nudge for one lane. */
|
|
161
|
-
readonly wake: (
|
|
160
|
+
readonly wake: (threadId: ThreadId) => Effect.Effect<void, OperationDenied>;
|
|
162
161
|
/** `DurableAgentRuntime.scanObligations` — the scan-based DUR-017/OPS-001 report. */
|
|
163
162
|
readonly scanObligations: (thresholds: ObligationThresholds) => Effect.Effect<ObligationReport, DurableObligationFailure>;
|
|
164
163
|
/**
|
|
@@ -189,18 +188,25 @@ declare const NodeDurableHost_base: Context.ServiceClass<NodeDurableHost, "@effe
|
|
|
189
188
|
* shutdown, provides correctness (DEPLOY-006).
|
|
190
189
|
*/
|
|
191
190
|
declare class NodeDurableHost extends NodeDurableHost_base {
|
|
192
|
-
/**
|
|
193
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Compile typed registrations and acquire the complete host in one Layer Scope.
|
|
193
|
+
* Node supplies Crypto; model, tool, instruction, and schema services remain required.
|
|
194
|
+
* Startup recovery and shutdown gates are unchanged. Workers start only when the caller
|
|
195
|
+
* runs runResolvedWorkers; this constructor never starts a background worker.
|
|
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 T_1) ? T_1 extends Entries[number] ? T_1 extends AgentRegistration<infer A extends import("@effect-agent/thread").ExecutableAgentBinding> ? import("@effect-agent/thread").DurableWorkerRequirements<A> : never : never : never, import("effect/Scope").Scope>, Crypto.Crypto>>;
|
|
198
|
+
/**
|
|
199
|
+
* Host gates over an assembled `NodeDurableRuntime` stack. Bindings must carry the exact
|
|
200
|
+
* digests stored by submitters. Omission registers no Agents, so resolved work fails closed.
|
|
201
|
+
*/
|
|
202
|
+
static readonly layer: (bindings?: ReadonlyArray<ResolvedBinding>) => Layer.Layer<NodeDurableHost, DurableWorkerFailure, DurableAgentRuntime | NodeDurableRuntimeConfig>;
|
|
194
203
|
/** The complete DN host: `NodeDurableRuntime.layer(options)` plus the host lifecycle gates. */
|
|
195
|
-
static layerStack(options: NodeDurableRuntimeOptions
|
|
204
|
+
static layerStack<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never>(options: NodeDurableRuntimeOptions<ContextError, ContextRequirements, AuthorizationError, AuthorizationRequirements> & {
|
|
205
|
+
readonly bindings?: ReadonlyArray<ResolvedBinding>;
|
|
206
|
+
}): Layer.Layer<NodeDurableHost | NodeDurableRuntimeServices, DurableWorkerFailure | NodeDurableRuntimeInitializationError | ContextError | AuthorizationError, Exclude<ContextRequirements | AuthorizationRequirements, Crypto.Crypto>>;
|
|
196
207
|
}
|
|
197
208
|
//#endregion
|
|
198
209
|
//#region src/scheduling.d.ts
|
|
199
|
-
/**
|
|
200
|
-
* Scheduled admission through the existing host gate. Once the gate admits the call, every
|
|
201
|
-
* runtime failure stays ambiguous because the Submission may already have committed.
|
|
202
|
-
*/
|
|
203
|
-
declare const nodeScheduledInputAdmissionLayer: Layer.Layer<ScheduledInputAdmission, never, NodeDurableHost>;
|
|
204
210
|
/** One bounded hint slot for the single supported Node scheduler. Indexed polling repairs loss. */
|
|
205
211
|
declare const nodeScheduleWakeLayer: Layer.Layer<ScheduleWake>;
|
|
206
212
|
interface NodeSchedulingOptions {
|
|
@@ -214,9 +220,24 @@ declare class NodeScheduling {
|
|
|
214
220
|
static layer(options?: NodeSchedulingOptions): Layer.Layer<Scheduling, ScheduleValidationError, NodeDurableHost | ScheduleStore | ScheduleAuthorizer>;
|
|
215
221
|
}
|
|
216
222
|
//#endregion
|
|
223
|
+
//#region src/subscriptions.d.ts
|
|
224
|
+
/** Ordinary prepared admission through the Scope-owned Node host gate. */
|
|
225
|
+
declare const nodePreparedInputAdmissionLayer: Layer.Layer<PreparedInputAdmission, never, NodeDurableHost>;
|
|
226
|
+
declare const nodeScheduledInputAdmissionLayer: Layer.Layer<ScheduledInputAdmission, never, NodeDurableHost>;
|
|
227
|
+
interface NodeSubscriptionsOptions {
|
|
228
|
+
readonly limits?: SubscriptionLimits | undefined;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* One Scope-owned subscription partition in the sole process owning its SQLite database.
|
|
232
|
+
* Indexed polling repairs restart and lost wake state; closing the Scope interrupts the driver.
|
|
233
|
+
*/
|
|
234
|
+
declare class NodeSubscriptions {
|
|
235
|
+
static layer(options?: NodeSubscriptionsOptions): Layer.Layer<Subscriptions | SubscriptionIntake, SubscriptionError, NodeDurableHost | SubscriptionStore | SubscriptionAuthorizer | EventSources | SubscriptionInputBindings>;
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
217
238
|
//#region src/wake-scheduler.d.ts
|
|
218
239
|
declare const NodeWakeSchedulerConfig_base: Context.ServiceClass<NodeWakeSchedulerConfig, "@effect-agent/platform-node/NodeWakeSchedulerConfig", {
|
|
219
|
-
/** Interval between ledger scans that re-emit every nonterminal
|
|
240
|
+
/** Interval between ledger scans that re-emit every nonterminal Thread lane. */
|
|
220
241
|
readonly scanInterval: Duration.Duration;
|
|
221
242
|
}>;
|
|
222
243
|
/** Cadence authority for the Node wake scheduler's ledger-scan fallback loop. */
|
|
@@ -234,5 +255,5 @@ declare class NodeWakeSchedulerConfig extends NodeWakeSchedulerConfig_base {
|
|
|
234
255
|
*/
|
|
235
256
|
declare const nodeWakeSchedulerLayer: Layer.Layer<WakeScheduler, never, SubmissionLedger | NodeWakeSchedulerConfig>;
|
|
236
257
|
//#endregion
|
|
237
|
-
export { AdmissionClosed, NodeDurableHost, NodeDurableRuntime, NodeDurableRuntimeConfig, NodeDurableRuntimeConfigValue, NodeDurableRuntimeInitializationError, NodeDurableRuntimeOptions, NodeDurableRuntimeServices, NodePlatformConfigError, NodeScheduling, NodeSchedulingOptions, NodeWakeSchedulerConfig, nodeScheduleWakeLayer, nodeScheduledInputAdmissionLayer, nodeWakeSchedulerLayer, ownershipDrainLayer };
|
|
258
|
+
export { AdmissionClosed, NodeDurableHost, NodeDurableRuntime, NodeDurableRuntimeConfig, NodeDurableRuntimeConfigValue, NodeDurableRuntimeInitializationError, NodeDurableRuntimeOptions, NodeDurableRuntimeServices, NodePlatformConfigError, NodeScheduling, NodeSchedulingOptions, NodeSubscriptions, NodeSubscriptionsOptions, NodeWakeSchedulerConfig, nodePreparedInputAdmissionLayer, nodeScheduleWakeLayer, nodeScheduledInputAdmissionLayer, nodeWakeSchedulerLayer, ownershipDrainLayer };
|
|
238
259
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Cause, Context, Duration, Effect, Layer, Option, PubSub, Ref, Result, Schema, Stream } from "effect";
|
|
3
|
-
import { CurrentToolFailureObserver, toolFailureObserverLayer } from "@effect-agent/engine";
|
|
4
|
-
import { SqliteStorageConfig, SqliteStorageConfigValue, conversationStoreLayer, scheduleStoreLayer, storageFailpointLayer, submissionLedgerLayer } from "@effect-agent/storage-sqlite";
|
|
1
|
+
import { DEFAULT_OWNERSHIP_LEASE_DURATION, DeploymentId, DurableAgentRuntime, DurableRuntimeConfig, DurableRuntimeFailpoint, PersistedJson, PreparedInputAdmission, ProducerId, ReleaseOwnershipRequest, ScheduleDriver, ScheduleStorageError, ScheduleStore, ScheduleWake, ScheduledInputAdmission, ScheduledInputRetryable, Scheduling, SubmissionLedger, SubscriptionDriver, SubscriptionIntake, SubscriptionStore, Subscriptions, ToolReconciler, WakeScheduler, compileRegistrations, defaultSchedulingLimits, defaultSubscriptionLimits, makeWakeSubscriptionHub } from "@effect-agent/thread";
|
|
5
2
|
import { NodeCrypto } from "@effect/platform-node";
|
|
3
|
+
import { Cause, Context, Duration, Effect, Exit, Layer, Option, PubSub, Ref, Result, Schema, Stream } from "effect";
|
|
4
|
+
import { CurrentToolFailureObserver, RunContextPreparationPassthrough, RunToolAuthorization, toolFailureObserverLayer } from "@effect-agent/engine";
|
|
5
|
+
import { SqliteStorageConfig, SqliteStorageConfigValue, scheduleStoreLayer, storageFailpointLayer, submissionLedgerLayer, threadStoreLayer } from "@effect-agent/storage-sqlite";
|
|
6
6
|
import { SqliteClient } from "@effect/sql-sqlite-node";
|
|
7
7
|
//#region src/wake-scheduler.ts
|
|
8
8
|
/**
|
|
@@ -23,13 +23,13 @@ const makeWakeScheduler = Effect.gen(function* () {
|
|
|
23
23
|
const progress = yield* makeWakeSubscriptionHub;
|
|
24
24
|
yield* Effect.addFinalizer(() => PubSub.shutdown(hints));
|
|
25
25
|
/**
|
|
26
|
-
* One fallback scan: every
|
|
26
|
+
* One fallback scan: every Thread lane with nonterminal work, deduplicated. A scan
|
|
27
27
|
* failure degrades to "no hints this round" — the wake channel has no error contract and the
|
|
28
28
|
* next round retries — but is logged so a persistently failing ledger stays visible.
|
|
29
29
|
*/
|
|
30
30
|
const scanOnce = Stream.runCollect(ledger.scanNonterminal).pipe(Effect.map((snapshots) => {
|
|
31
31
|
const lanes = /* @__PURE__ */ new Set();
|
|
32
|
-
for (const snapshot of snapshots) lanes.add(snapshot.
|
|
32
|
+
for (const snapshot of snapshots) lanes.add(snapshot.threadId);
|
|
33
33
|
return [...lanes];
|
|
34
34
|
}), Effect.catch((error) => Effect.logWarning("NodeWakeScheduler fallback scan failed", error).pipe(Effect.as([]))));
|
|
35
35
|
/**
|
|
@@ -39,7 +39,7 @@ const makeWakeScheduler = Effect.gen(function* () {
|
|
|
39
39
|
*/
|
|
40
40
|
const fallbackScans = Stream.fromIterableEffectRepeat(Effect.sleep(config.scanInterval).pipe(Effect.andThen(scanOnce)));
|
|
41
41
|
return WakeScheduler.of({
|
|
42
|
-
notify: (
|
|
42
|
+
notify: (threadId) => progress.notify(threadId).pipe(Effect.andThen(PubSub.publish(hints, threadId)), Effect.asVoid),
|
|
43
43
|
subscribe: progress.subscribe,
|
|
44
44
|
wakes: Stream.merge(Stream.fromPubSub(hints), fallbackScans)
|
|
45
45
|
});
|
|
@@ -68,7 +68,7 @@ var NodePlatformConfigError = class extends Schema.TaggedError()("NodePlatformCo
|
|
|
68
68
|
* finite; `workerConcurrency` caps how many worker loops `NodeDurableHost.runWorkers` drives.
|
|
69
69
|
*/
|
|
70
70
|
var NodeDurableRuntimeConfigValue = class extends Schema.Class("@effect-agent/platform-node/NodeDurableRuntimeConfigValue")({
|
|
71
|
-
/** SQLite database file backing BOTH the
|
|
71
|
+
/** SQLite database file backing BOTH the Thread Log and the Submission Ledger. */
|
|
72
72
|
filename: Schema.NonEmptyString,
|
|
73
73
|
deploymentId: DeploymentId,
|
|
74
74
|
producerId: ProducerId,
|
|
@@ -121,7 +121,7 @@ const sqliteStorageConfigLayer = Layer.effect(SqliteStorageConfig)(Effect.gen(fu
|
|
|
121
121
|
verifyOnOpen: config.verifyOnOpen
|
|
122
122
|
});
|
|
123
123
|
}));
|
|
124
|
-
/**
|
|
124
|
+
/** Thread coordinator configuration derived from the single validated Node configuration. */
|
|
125
125
|
const durableRuntimeConfigLayer = (estimateCostMicrousd) => Layer.effect(DurableRuntimeConfig)(Effect.gen(function* () {
|
|
126
126
|
const config = yield* NodeDurableRuntimeConfig;
|
|
127
127
|
return DurableRuntimeConfig.make({
|
|
@@ -198,7 +198,7 @@ const ownershipDrainLayer = Layer.effect(SubmissionLedger)(Effect.gen(function*
|
|
|
198
198
|
/**
|
|
199
199
|
* The DN Layer assembly (deployment §12: a Layer-assembly library, not an app entrypoint).
|
|
200
200
|
* `layer(options)` decodes the configuration, opens ONE SQLite database serving both the
|
|
201
|
-
*
|
|
201
|
+
* Thread Log and the Submission Ledger (so claims fence the same producer epochs), wires
|
|
202
202
|
* the Node wake scheduler with its ledger-scan fallback, wraps the ledger with the shutdown
|
|
203
203
|
* ownership drain, defaults the Tool reconciliation policy to the fail-closed
|
|
204
204
|
* `ToolReconciler.uncertain` (override via `options.toolReconciler`), and provides a ready
|
|
@@ -222,9 +222,8 @@ var NodeDurableRuntime = class {
|
|
|
222
222
|
const runtimeFailpointLayer = options.runtimeFailpoint === void 0 ? DurableRuntimeFailpoint.layer : Layer.succeed(DurableRuntimeFailpoint)({ hit: options.runtimeFailpoint });
|
|
223
223
|
const reconcilerLayer = options.toolReconciler ?? ToolReconciler.uncertain;
|
|
224
224
|
const observerLayer = options.toolFailureObserver === void 0 ? Layer.succeed(CurrentToolFailureObserver)(void 0) : toolFailureObserverLayer(options.toolFailureObserver);
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
return DurableAgentRuntime.layer.pipe(Layer.provideMerge(Layer.mergeAll(ports, durableRuntimeConfigLayer(options.estimateCostMicrousd), bindingResolverLayer)), Layer.provide(Layer.mergeAll(wakeSchedulerConfigLayer, runtimeFailpointLayer, reconcilerLayer, observerLayer)), Layer.provideMerge(infrastructure), Layer.provideMerge(nodeConfigLayer));
|
|
225
|
+
const ports = Layer.mergeAll(threadStoreLayer, scheduleStoreLayer, nodeWakeSchedulerLayer.pipe(Layer.provideMerge(ownershipDrainLayer.pipe(Layer.provide(submissionLedgerLayer)))));
|
|
226
|
+
return DurableAgentRuntime.layerWithServices.pipe(Layer.provideMerge(Layer.mergeAll(ports, durableRuntimeConfigLayer(options.estimateCostMicrousd))), Layer.provide(Layer.mergeAll(wakeSchedulerConfigLayer, runtimeFailpointLayer, reconcilerLayer, observerLayer)), Layer.provideMerge(infrastructure), Layer.provideMerge(nodeConfigLayer), Layer.provide(Layer.mergeAll(options.runContext ?? RunContextPreparationPassthrough, options.toolAuthorization ?? RunToolAuthorization.allowAll).pipe(Layer.provide(NodeCrypto.layer))));
|
|
228
227
|
}));
|
|
229
228
|
}
|
|
230
229
|
};
|
|
@@ -235,10 +234,9 @@ var NodeDurableRuntime = class {
|
|
|
235
234
|
* Accepted work is unaffected — only NEW admissions are refused.
|
|
236
235
|
*/
|
|
237
236
|
var AdmissionClosed = class extends Schema.TaggedError()("AdmissionClosed", { message: Schema.String }) {};
|
|
238
|
-
const makeHost = Effect.gen(function* () {
|
|
237
|
+
const makeHost = (bindings) => Effect.gen(function* () {
|
|
239
238
|
const runtime = yield* DurableAgentRuntime;
|
|
240
239
|
const config = yield* NodeDurableRuntimeConfig;
|
|
241
|
-
const bindingResolver = yield* AgentBindingResolver;
|
|
242
240
|
const startupRecovery = yield* runtime.runRecovery;
|
|
243
241
|
const admission = yield* Ref.make(true);
|
|
244
242
|
yield* Effect.addFinalizer(() => Ref.set(admission, false));
|
|
@@ -248,7 +246,7 @@ const makeHost = Effect.gen(function* () {
|
|
|
248
246
|
concurrency: "unbounded",
|
|
249
247
|
discard: true
|
|
250
248
|
});
|
|
251
|
-
const runResolvedWorkers = runWorkers(runtime.runResolvedWorker
|
|
249
|
+
const runResolvedWorkers = runWorkers(runtime.runResolvedWorker(bindings));
|
|
252
250
|
return NodeDurableHost.of({
|
|
253
251
|
startupRecovery,
|
|
254
252
|
admissionOpen: Ref.get(admission),
|
|
@@ -257,7 +255,7 @@ const makeHost = Effect.gen(function* () {
|
|
|
257
255
|
observe: runtime.observe,
|
|
258
256
|
abort: runtime.abort,
|
|
259
257
|
explain: runtime.explain,
|
|
260
|
-
|
|
258
|
+
explainThread: runtime.explainThread,
|
|
261
259
|
verify: runtime.verify,
|
|
262
260
|
retry: runtime.retry,
|
|
263
261
|
wake: runtime.wake,
|
|
@@ -281,15 +279,31 @@ const makeHost = Effect.gen(function* () {
|
|
|
281
279
|
* shutdown, provides correctness (DEPLOY-006).
|
|
282
280
|
*/
|
|
283
281
|
var NodeDurableHost = class NodeDurableHost extends Context.Service()("@effect-agent/platform-node/NodeDurableHost") {
|
|
284
|
-
/**
|
|
285
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Compile typed registrations and acquire the complete host in one Layer Scope.
|
|
284
|
+
* Node supplies Crypto; model, tool, instruction, and schema services remain required.
|
|
285
|
+
* Startup recovery and shutdown gates are unchanged. Workers start only when the caller
|
|
286
|
+
* runs runResolvedWorkers; this constructor never starts a background worker.
|
|
287
|
+
*/
|
|
288
|
+
static layerRegistered(registrations, options) {
|
|
289
|
+
return Layer.unwrap(Effect.map(compileRegistrations(registrations), (bindings) => NodeDurableHost.layerStack({
|
|
290
|
+
...options,
|
|
291
|
+
bindings
|
|
292
|
+
}))).pipe(Layer.provide(NodeCrypto.layer));
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Host gates over an assembled `NodeDurableRuntime` stack. Bindings must carry the exact
|
|
296
|
+
* digests stored by submitters. Omission registers no Agents, so resolved work fails closed.
|
|
297
|
+
*/
|
|
298
|
+
static layer = (bindings = []) => Layer.effect(NodeDurableHost)(makeHost(bindings));
|
|
286
299
|
/** The complete DN host: `NodeDurableRuntime.layer(options)` plus the host lifecycle gates. */
|
|
287
300
|
static layerStack(options) {
|
|
288
|
-
|
|
301
|
+
const { bindings = [], ...runtimeOptions } = options;
|
|
302
|
+
return NodeDurableHost.layer(bindings).pipe(Layer.provideMerge(NodeDurableRuntime.layer(runtimeOptions)));
|
|
289
303
|
}
|
|
290
304
|
};
|
|
291
305
|
//#endregion
|
|
292
|
-
//#region src/
|
|
306
|
+
//#region src/subscriptions.ts
|
|
293
307
|
const passthroughSubmitAgent = (agentId) => ({ definition: {
|
|
294
308
|
id: agentId,
|
|
295
309
|
input: PersistedJson
|
|
@@ -299,30 +313,79 @@ const corrupt = (operation) => ScheduleStorageError.make({
|
|
|
299
313
|
operation,
|
|
300
314
|
reason: "corrupt"
|
|
301
315
|
});
|
|
302
|
-
/**
|
|
303
|
-
|
|
304
|
-
* runtime failure stays ambiguous because the Submission may already have committed.
|
|
305
|
-
*/
|
|
306
|
-
const nodeScheduledInputAdmissionLayer = Layer.effect(ScheduledInputAdmission, Effect.gen(function* () {
|
|
316
|
+
/** Ordinary prepared admission through the Scope-owned Node host gate. */
|
|
317
|
+
const nodePreparedInputAdmissionLayer = Layer.effect(PreparedInputAdmission, Effect.gen(function* () {
|
|
307
318
|
const host = yield* NodeDurableHost;
|
|
308
|
-
return
|
|
309
|
-
|
|
319
|
+
return PreparedInputAdmission.of({ submit: (envelope) => host.submit(passthroughSubmitAgent(envelope.agentId), envelope.input, {
|
|
320
|
+
threadId: envelope.threadId,
|
|
310
321
|
principal: envelope.deliveryPrincipal,
|
|
311
322
|
idempotencyKey: envelope.admissionKey,
|
|
312
323
|
definitions: envelope.definitions
|
|
313
324
|
}).pipe(Effect.catchTags({
|
|
314
325
|
AdmissionClosed: () => Effect.fail(ScheduledInputRetryable.make({ reason: "host-closed" })),
|
|
315
|
-
AgentInputError: () => Effect.fail(corrupt("
|
|
316
|
-
AdmissionConflict: () => Effect.fail(corrupt("
|
|
326
|
+
AgentInputError: () => Effect.fail(corrupt("prepared admission input")),
|
|
327
|
+
AdmissionConflict: () => Effect.fail(corrupt("prepared admission conflict")),
|
|
317
328
|
DigestError: () => Effect.fail(ambiguous()),
|
|
318
329
|
LedgerError: () => Effect.fail(ambiguous()),
|
|
319
|
-
|
|
320
|
-
|
|
330
|
+
ThreadStoreError: () => Effect.fail(ambiguous()),
|
|
331
|
+
ThreadNotMaterialized: () => Effect.fail(ambiguous()),
|
|
321
332
|
AppendConflict: () => Effect.fail(ambiguous()),
|
|
322
333
|
FenceRejected: () => Effect.fail(ambiguous()),
|
|
323
334
|
DurableRuntimeFailpointError: () => Effect.fail(ambiguous())
|
|
324
335
|
})) });
|
|
325
336
|
}));
|
|
337
|
+
const preparedFromSchedule = (envelope) => ({
|
|
338
|
+
schemaVersion: 1,
|
|
339
|
+
threadId: envelope.threadId,
|
|
340
|
+
deliveryPrincipal: envelope.deliveryPrincipal,
|
|
341
|
+
agentId: envelope.agentId,
|
|
342
|
+
definitions: envelope.definitions,
|
|
343
|
+
input: envelope.input,
|
|
344
|
+
inputDigest: envelope.inputDigest,
|
|
345
|
+
admissionKey: envelope.admissionKey,
|
|
346
|
+
authorization: envelope.authorization
|
|
347
|
+
});
|
|
348
|
+
const nodeScheduledInputAdmissionLayer = Layer.effect(ScheduledInputAdmission, Effect.map(PreparedInputAdmission, (admission) => ScheduledInputAdmission.of({ submit: (envelope) => admission.submit(preparedFromSchedule(envelope)) }))).pipe(Layer.provide(nodePreparedInputAdmissionLayer));
|
|
349
|
+
const reportPassFailure$1 = (cause) => Cause.hasInterruptsOnly(cause) ? Effect.interrupt : Effect.logWarning("Node subscription pass failed").pipe(Effect.annotateLogs({ failureTag: Option.match(Cause.findErrorOption(cause), {
|
|
350
|
+
onNone: () => "Defect",
|
|
351
|
+
onSome: (error) => error._tag
|
|
352
|
+
}) }), Effect.as(false));
|
|
353
|
+
const nodeSubscriptionDriverLayer = (limits) => Layer.effectDiscard(Effect.gen(function* () {
|
|
354
|
+
const driver = yield* SubscriptionDriver;
|
|
355
|
+
const store = yield* SubscriptionStore;
|
|
356
|
+
const run = Effect.gen(function* () {
|
|
357
|
+
while (true) {
|
|
358
|
+
if (!(yield* driver.runDue.pipe(Effect.map((pass) => pass.failed === 0), Effect.catchCause(reportPassFailure$1)))) {
|
|
359
|
+
yield* Effect.sleep(Duration.millis(limits.retryMillis));
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const deadline = yield* store.nextDeadline.pipe(Effect.exit);
|
|
363
|
+
if (Exit.isFailure(deadline)) {
|
|
364
|
+
yield* reportPassFailure$1(deadline.cause);
|
|
365
|
+
yield* Effect.sleep(Duration.millis(limits.retryMillis));
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const nowMillis = yield* Effect.clockWith((clock) => clock.currentTimeMillis);
|
|
369
|
+
const delay = deadline.value === null ? limits.retryMillis : Math.max(1, Math.min(deadline.value - nowMillis, limits.retryMillis));
|
|
370
|
+
yield* Effect.sleep(Duration.millis(delay));
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
yield* Effect.forkScoped(run);
|
|
374
|
+
}));
|
|
375
|
+
/**
|
|
376
|
+
* One Scope-owned subscription partition in the sole process owning its SQLite database.
|
|
377
|
+
* Indexed polling repairs restart and lost wake state; closing the Scope interrupts the driver.
|
|
378
|
+
*/
|
|
379
|
+
var NodeSubscriptions = class {
|
|
380
|
+
static layer(options = {}) {
|
|
381
|
+
const limits = options.limits ?? defaultSubscriptionLimits;
|
|
382
|
+
const publicServices = Layer.merge(Subscriptions.layer(limits), SubscriptionIntake.layer(limits));
|
|
383
|
+
const driver = nodeSubscriptionDriverLayer(limits).pipe(Layer.provide(SubscriptionDriver.layer(limits)));
|
|
384
|
+
return Layer.merge(publicServices, driver).pipe(Layer.provide(nodePreparedInputAdmissionLayer), Layer.provide(NodeCrypto.layer));
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region src/scheduling.ts
|
|
326
389
|
/** One bounded hint slot for the single supported Node scheduler. Indexed polling repairs loss. */
|
|
327
390
|
const nodeScheduleWakeLayer = Layer.effect(ScheduleWake, Effect.gen(function* () {
|
|
328
391
|
const hints = yield* PubSub.sliding(1);
|
|
@@ -368,6 +431,6 @@ var NodeScheduling = class {
|
|
|
368
431
|
}
|
|
369
432
|
};
|
|
370
433
|
//#endregion
|
|
371
|
-
export { AdmissionClosed, NodeDurableHost, NodeDurableRuntime, NodeDurableRuntimeConfig, NodeDurableRuntimeConfigValue, NodePlatformConfigError, NodeScheduling, NodeWakeSchedulerConfig, nodeScheduleWakeLayer, nodeScheduledInputAdmissionLayer, nodeWakeSchedulerLayer, ownershipDrainLayer };
|
|
434
|
+
export { AdmissionClosed, NodeDurableHost, NodeDurableRuntime, NodeDurableRuntimeConfig, NodeDurableRuntimeConfigValue, NodePlatformConfigError, NodeScheduling, NodeSubscriptions, NodeWakeSchedulerConfig, nodePreparedInputAdmissionLayer, nodeScheduleWakeLayer, nodeScheduledInputAdmissionLayer, nodeWakeSchedulerLayer, ownershipDrainLayer };
|
|
372
435
|
|
|
373
436
|
//# sourceMappingURL=index.mjs.map
|