@yielded/agent-workflow drives the durable agent runtime through upstream Effect Workflow.
Supply a WorkflowEngine Layer, durable dispatch storage, and a host-owned repair trigger.
The agent definitions, registrations, and durable recovery rules stay the same when you
replace the engine Layer.
The shared host has no Node.js or Cloudflare dependency. Platform adapters supply storage
and repair scheduling. The Node.js setup below uses SQLite and a single-process
Cluster runner.
Author workflows with Effect’s Workflow.make, toLayer, and execute.
AgentWorkflow.execute runs a registered Agent as an Effect inside the handler and returns
its Schema-decoded output. A pending Agent suspends the handler through Effect’s
DurableDeferred; approval resolution and settlement resume it without keeping a polling
fiber alive in the parent.
}>,"Classify the severity of the bug report.",Toolkit.Toolkit<{}>,undefined,undefined,undefined>&{
readonlyinputPrompt?:undefined;
readonlyrunDisposition?:undefined;
}):Agent.Definition<...>&{
...;
}(+3overloads)
Validate an agent ID and return a shallowly frozen, model-agnostic definition.
make("triage",{
DefinitionOptions<String,Struct<{ readonlyseverity:Literals<readonly ["low","high","critical"]>; }>,"Classify the severity of the bug report.",Toolkit<{}>,undefined,undefined,undefined>.input: Schema.String
input:
importSchema
Schema.
constString:Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof"string".
@category ― models
@since ― 4.0.0
@category ― schemas
@since ― 4.0.0
String,
DefinitionOptions<String,Struct<{ readonlyseverity:Literals<readonly ["low","high","critical"]>; }>,"Classify the severity of the bug report.",Toolkit<{}>,undefined,undefined,undefined>.output: Schema.Struct<{
Defines a struct schema from a map of field schemas.
Details
Each field value is a schema. Use
optionalKey
or
optional
to
mark fields as optional, and
mutableKey
to mark them as mutable.
The resulting schema's Type is a readonly object type with the fields'
decoded types. The Encoded form mirrors the field schemas' encoded types.
Declared fields may be inherited and are copied to own properties in the
output. The __proto__ field is accepted only when it is an own property.
Parsing does not guarantee that output keys retain their input order.
@see ― Literal for a schema that represents a single literal.
@category ― constructors
@since ― 4.0.0
Literals(["low","high","critical"])}),
DefinitionOptions<String,Struct<{ readonlyseverity:Literals<readonly ["low","high","critical"]>; }>,"Classify the severity of the bug report.",Toolkit<{}>,undefined,undefined,undefined>.instructions: "Classify the severity of the bug report."
instructions:"Classify the severity of the bug report.",
DefinitionOptions<String,Struct<{ readonlyseverity:Literals<readonly ["low","high","critical"]>; }>,"Classify the severity of the bug report.",Toolkit<{}>,undefined,undefined,undefined>.toolkit: Toolkit.Toolkit<{}>
toolkit:
importToolkit
Toolkit.
constempty:Toolkit.Toolkit<{}>
An empty toolkit with no tools.
When to use
Use when you need an empty starting point for building toolkits or a default
toolkit value that can be extended with merge.
Definition<String,Struct<{ readonlyseverity:Literals<readonly ["low","high","critical"]>; }>,"Classify the severity of the bug report.",Toolkit<{}>,undefined,undefined,undefined>.output: Schema.Struct<{
Run a registered agent inside a native Workflow.toLayer handler. One stable name identifies
one submission in that parent execution. Replays verify admission identity and decode the
canonical output again. Pending work suspends through Effect DurableDeferred; neither
suspension nor parent interruption aborts accepted agent work. Use the host's authorized abort.
Provide WorkflowAgentHost at the application boundary, sharing the parent's WorkflowEngine.
Pass the exact Agent Definition instance used in runtime registration; a same-ID copy fails
with BindingUnavailable before input encoding or admission.
Register triage and its model in the durable runtime used by your host. Supply the resulting host Layer
to ReviewLive with Layer.provideMerge, then provide that Layer to review. Share one
WorkflowEngine Layer between the parent and host; a mismatched engine fails before admission.
Multi-step handlers use ordinary Effect.gen and bounded Effect.all.
The step name must be nonempty, stable across replays, and unique within a parent execution.
Use stable item IDs for repeated calls in a loop. The parent workflow identity, step name,
deployment, and configured principal determine a private Thread and submission key. Reusing a
step with changed input, Agent identity, or registered version declarations fails with an admission
conflict rather than silently starting new work. Registered admission requires exactly one
version for that Agent identity and the exact Agent Definition instance passed to registration.
A same-ID copy or replacement fails with BindingUnavailable before input encoding or admission,
including on replay. Import the same definition into registration and the workflow handler;
there is no object identity persisted across restarts. Explicit host.submit remains available
for versioned routing.
AgentWorkflow.Error is the Schema for the exact typed failure channel. Failed or aborted
Agent settlements become WorkflowExecutionFailure; invalid output becomes AgentOutputError.
Admission, authorization, and dispatch failures retain their original tags. Infrastructure
failure can occur after admission; retries with the same step identity reconnect to the accepted
work. Choose retry behavior with ordinary Effect combinators. Defects in the underlying durable
driver retain its existing suspension and repair behavior.
Parent interruption, timeout, or shutdown detaches the parent; it does not abort accepted Agent
work. Use the host’s authorized abort command to cancel the Agent. Native compensation does
not undo external tool effects. Ordinary uncertain tools still require explicit resolution.
WorkflowAgentHost.layer(options) consumes these services through Layers:
Service
Responsibility
DurableAgentRuntime, SubmissionLedger, and DurableRuntimeConfig
Agent execution, admission, canonical history, recovery, and deployment identity
WorkflowEngine
Native Workflow execution and persistence
WorkflowDispatchStore
Durable dispatch intents retained until completion is verified
WorkflowRepairTrigger
Startup and repeated repair after lost hints or host restarts
The runtime Layer owns executable registrations and their model, tool, instruction, and
schema services. Supply those application services and the host’s Crypto service through
ordinary Layer composition. The Workflow handler passes only a Thread ID to
processThreadHead; it cannot replace captured model or tool services on an execution.
Keep deploymentId identical in both runtime and Workflow host options. The optional
workflowName is a stable versioned prefix, defaulting to @yielded/agent/submission/v1.
The native name appends /deployment/<length>:<deploymentId>. Keep one host registration per
deployment, name, and engine. Changing that identity leaves the old dispatch obligations for
their original host to repair.
The required principal is application-owned authority for AgentWorkflow.execute, never
model-supplied input. Model and tool services remain captured by runtime registration; handler
services cannot replace them. Input encoding and output decoding requirements remain visible in
the execution Effect. Each result read rechecks authorization, including on workflow replay.
Admit durable work, persist its dispatch intent, and request native execution
awaitSettlement(receipt)
Wait for the canonical terminal outcome
observe(receipt)
Stream canonical records through the runtime’s authorization policy
submissionStatus(receipt)
Read authorized pending or settled status without waiting
abort(command)
Record authorized abort intent without replacing an existing Settlement
resolveApproval(command)
Record an authorized approval decision for later processing
resolveUnknown(command)
Record an authorized resolution of uncertain external work
A dispatch error or timeout can occur after admission. Retry the same input with the same
idempotency key; accepted work remains recoverable. A receipt identifies work and grants no
authorization by itself. Interrupting a waiter or observer only detaches that caller.
Use abort for cancellation; native Workflow interruption does not implement durable abort.
Approval and unknown-outcome resolutions resume through repair.
The shared Workflow host has no internal poll loop. Every host must provide a
WorkflowRepairTrigger that invokes repair at startup and continues after lost hints and
host restarts. It must stop invoking repair when its Scope closes. host.repair is also
available for explicit bounded repair.
Each repair pass selects at most repairBatchSize accepted submissions and that many dispatch
intents. Each scan and each item has the dispatchTimeoutMillis bound. Failed items leave their
obligations intact while other items can advance. Dispatch intents remain until native success
references the matching canonical Settlement, even if the submission ledger already settled.
For AgentWorkflow.execute, the intent also retains the parent’s durable completion token.
Repair delivers the canonical reference through DurableDeferred before removing the intent.
Custom stores must atomically attach one token, preserve it on subsequent put calls, return
the retained intent, and compare the entire intent before removal. A stale cleanup must fail
instead of erasing a newly attached notification obligation.
Admission, dispatch persistence, and native Workflow persistence are separate commits. Never
enclose agent execution in a SQL transaction.
Pending status, an empty processing result, or Workflow suspension is not completion.
Infrastructure failures suspend native execution; repeated repair drives recovery and resumes.
Ordinary tools are not wrapped in Activities and retain the unknown-outcome rules described in
durability.
executionConcurrency limits Attempts in this host Layer instance, not across a fleet.
Within that host, only one recovery or processing pass runs for a Thread at a time.
Each Attempt owns its resources and releases ownership and permits before native suspension.
Closing the host Scope stops its repair trigger and closes acquired resources.
This example reuses node-agent.ts from the Node.js guide,
including its model client and registration versions.
This assembly uses ClusterWorkflowEngine with SingleRunner on one Node process.
runnerStorage: "memory" keeps runner bookkeeping in memory; native messages and replies still
persist in SQL. The dispatch store shares that SQL connection. Canonical agent history and the
submission ledger use a separate SQLite file.
import{
classNodeDurableAgentRuntime
The DN Layer assembly (deployment §12: a Layer-assembly library, not an app entrypoint).
layer(options) decodes the configuration, opens ONE SQLite database serving both the
Thread Log and the Submission Ledger (so claims fence the same producer epochs), wires
the Node wake scheduler with its ledger-scan fallback, exposes independent message delivery
storage, wraps the ledger with the shutdown
ownership drain, defaults the Tool reconciliation policy to the fail-closed
ToolReconciler.uncertain (override via options.toolReconciler), and provides a ready
DurableAgentRuntime on top. Storage compatibility is
verified during construction: an incompatible database file fails the Layer with
SqliteStorageCompatibilityError before anything is mutated (DEPLOY-008).
A host-scoped startup and polling trigger. No ordinary Node agent worker is started.
NodeWorkflowRepairTrigger,
classSqlWorkflowDispatchStore
Durable dispatch outbox over an application-supplied SqlClient. This adapter uses
SQLite/PostgreSQL SQL syntax and is certified with SQLite. It does not own an engine
or a database connection. Agent admission, dispatch persistence, and native Workflow
storage are separate commits; the registered repair trigger closes those gaps.
Stored version or shape mismatches fail typed and require an explicit data reset.
Optional engine-independent host. Supply the upstream WorkflowEngine, the existing
durable runtime and ledger, durable dispatch storage, and a host-owned repair trigger.
Do not also start the ordinary Node worker loop. Waiter interruption only detaches;
abort and resolutions retain the runtime's authorization and durable intent protocol.
Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Provides a SQL-backed single-node cluster for running durable
entities and workflows.
When to use
Use to run durable cluster entities and workflows in a local, embedded, or
small single-node process while keeping mailbox and reply state in SQL.
Details
The layer provides Sharding, Runners, and MessageStorage. It loads
ShardingConfig from environment variables and overlays
options.shardingConfig when provided. Message storage is always SQL-backed;
runner storage is SQL-backed by default and switches to in-memory storage
when runnerStorage is set to "memory".
Gotchas
Even when runnerStorage is "memory", message storage remains
SQL-backed, so callers must still provide SqlClient and Crypto.Crypto
(used to hash over-length message deduplication keys).
Runner communication and runner health are no-op services, so this layer is
for single-process use rather than multi-runner coordination.
@see ― ShardingConfig.layerFromEnv for loading environment configuration before applying shardingConfig overrides
@see ― SqlMessageStorage.layer for the SQL-backed message storage that this layer provides
@see ― SqlRunnerStorage.layer for the default SQL-backed runner storage selected when runnerStorage is omitted or "sql"
@see ― RunnerStorage.layerMemory for the in-memory runner storage selected by runnerStorage: "memory"
Combines all the provided layers concurrently, creating a new layer with
merged input, error, and output types.
When to use
Use when you need to combine multiple independent layers.
Details
All layers are built concurrently, and their outputs are merged into a single layer.
If multiple merged layers depend on the same layer value, that dependency is
shared by default. Reuse a named layer value when you want services to share
the same resource, such as one database pool.
Durable dispatch outbox over an application-supplied SqlClient. This adapter uses
SQLite/PostgreSQL SQL syntax and is certified with SQLite. It does not own an engine
or a database connection. Agent admission, dispatch persistence, and native Workflow
storage are separate commits; the registered repair trigger closes those gaps.
Stored version or shape mismatches fail typed and require an explicit data reset.
Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Optional engine-independent host. Supply the upstream WorkflowEngine, the existing
durable runtime and ledger, durable dispatch storage, and a host-owned repair trigger.
Do not also start the ordinary Node worker loop. Waiter interruption only detaches;
abort and resolutions retain the runtime's authorization and durable intent protocol.
Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Effect.runSync(program)// => { id: "123", name: "DB: SELECT * FROM users WHERE id = 123" }
logs// => ["[LOG] Looking up user 123"]
@see ― provideMerge for retaining the dependency services
@category ― providing services
@since ― 2.0.0
provide(
classNodeDurableAgentRuntime
The DN Layer assembly (deployment §12: a Layer-assembly library, not an app entrypoint).
layer(options) decodes the configuration, opens ONE SQLite database serving both the
Thread Log and the Submission Ledger (so claims fence the same producer epochs), wires
the Node wake scheduler with its ledger-scan fallback, exposes independent message delivery
storage, wraps the ledger with the shutdown
ownership drain, defaults the Tool reconciliation policy to the fail-closed
ToolReconciler.uncertain (override via options.toolReconciler), and provides a ready
DurableAgentRuntime on top. Storage compatibility is
verified during construction: an incompatible database file fails the Layer with
SqliteStorageCompatibilityError before anything is mutated (DEPLOY-008).
Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Feeds the output services of the dependency layer into the requirements of
this layer, returning a layer that only provides the services from this layer.
When to use
Use when you need to hide an implementation dependency layer from callers.
Details
In serviceLayer.pipe(Layer.provide(dependencyLayer)), the dependency layer is
built first and is used to satisfy the requirements of serviceLayer.
Provide the remaining model, tool, instruction, and schema services to this Layer, then share it
with the application effects that use WorkflowAgentHost. Its inferred types retain
construction errors and application requirements. Acquiring it registers native execution and
starts repair. Do not also start the ordinary NodeDurableHost worker loop for this deployment.
NodeWorkflowRepairTrigger runs repair at startup and at the configured interval within the
host Scope.
See the Node.js guide for registration and storage
configuration, and runtime services for context
preparation and tool authorization.
This SQL assembly is certified for a single Node process. Its runner configuration and
concurrency limit do not establish multi-runner support.