An agent definition contains schemas, instructions, a native Effect AI toolkit, and a finite
policy. The application supplies model services and other dependencies when it runs the agent.
Definitions contain no provider client, database connection, mutable thread, or acquired
resource. Reuse one definition across many runs. Effect Schema supplies the data types and runtime
validation; provider wire schemas derive from those definitions.
Agent.make requires an ID, input and output schemas, instructions, and a toolkit.
Declare ordinary limits as a plain policy object; Agent.make validates it and fills defaults.
A complete AgentPolicy value is also accepted. Standalone defaults are
12 turns, 24 tool calls, 5 minutes, and tool concurrency 4. Other defaults follow AgentPolicy.make.
Delegated children inherit omitted policy fields from their parent. Supply a partial object
to inherit individual fields; AgentPolicy.make fills its own defaults before inheritance.
Definitions retain the explicit fields as policyOverrides; policy holds their standalone values.
It also accepts inputPrompt, completion, runDisposition, a description, and metadata.
Instructions may be static prompt input or a function of decoded input. That function may return
an Effect. Its errors and service requirements become part of the run type.
Output Schemas use JSON final messages by default. For ordinary assistant text, wrap the complete
Schema with Output.text. The Schema must encode as a string; its checks, transformations, and
service requirements still apply.
Declare ordinary assistant text as an Agent's final-output wire format. Apply this to the
complete output Schema after composing transformations. Its encoded type must be string;
decoding, checks, transformations, and service requirements remain owned by that Schema.
Text is preserved verbatim, including whitespace, quotes, and an empty reply when allowed.
Required completion Tools still take precedence. Unmarked Schemas use JSON final output.
Validates that a value has at most the specified length. Works with strings
and arrays.
Details
JSON Schema:
The bound must be finite; it is rounded down and clamped to zero. This check
corresponds to maxItems for arrays. Strings use the same bound for
maxLength, which cannot reject a string accepted by this check because a
string has no more code points than UTF-16 code units.
Arbitrary:
During arbitrary generation, this applies a maxLength
constraint to ensure generated strings or arrays have at most the required
length.
@category ― validation
@since ― 4.0.0
isMaxLength(20_000)));
// Pass Reply as Agent.make(..., { output: Reply, ... }).
The runtime instructs the model to reply without JSON wrapping and decodes that text directly.
Whitespace, quotes, and empty replies are preserved. Use Schema.NonEmptyString when silence is
invalid. Apply Output.text after composing the Schema. Required completion tools still take
precedence; optional completion tools retain their own parameter contract. Durable records store
the validated string as a JSON string value. Metadata does not select an output format.
Once RunCompleted records validated output, recovery preserves that stored
value without decoding it through a later output Schema.
inputPrompt receives decoded input and returns native Effect AI Prompt.RawInput, directly or
through an Effect. Strings become user messages. Prompts and message arrays keep their roles,
parts, provider options, and multimodal content. Return an empty Prompt or message array to omit
the input message. Its errors and service requirements join the run’s E and R.
The runtime builds the source from history, instructions, and projected input, then applies context
preparation and compaction. Outgoing requests place system instructions and the output contract
before the conversation; optional transient context and run status follow the conversation. See
Context management.
Projection changes only model-visible input. Durable admission still stores the complete encoded
input, and tool authorization receives it. Keep secrets out of instructions and apply separate
disclosure rules to history, tool results, steering, and host context transforms.
Durable attempts may evaluate the projection again. Committed turns keep their recorded projected
messages. Projection effects must tolerate another evaluation and must not assume exactly-once
external execution.
A projection failure stops the run before the next model request. The runtime never falls back to
the full input. Projection services, errors, interruption, and deadlines follow normal Effect
semantics.
Provides dependencies to an effect using layers or a context. Use options.local
to build the layer every time; by default, layers are shared between provide
calls.
Provides a layer or context to the stream, removing the corresponding
service requirements. Use options.local to build the layer every time; by
default, layers are shared between provide calls.
Build the Toolkit handler Layer using the provided model requirement, or pass
a native model / explicit child Binding as an override. Without an override,
provide the model with Layer.provide; the handler captures it at construction.
AutoModel resolves each new child's own Thread ID and projected first task.
Share its selection store across parent Runs and child handler Layers.
Construction requirements carry the child Binding's full runtime needs and
both projections; they are captured once via Effect.context so the
per-call handler requirements stay exactly the Tool's declared engine
dependencies. The handler dispatches on the engine-provided per-batch
SubagentDurability service mode:
ephemeral (the explicit engine default when no durable coordinator
supplied RunOptions.subagent): the S1 path unchanged — preflight, an
in-process scoped child Run (SUB-011/012), stable lifecycle events,
total-mapped expected child failures, and Scope-finalizer reservation
settlement on every exit path.
durable (S2): the same fail-closed preflight and input projection,
then idempotent establishment through the coordinator (spec §12 steps
2-9) with construction-fixed child Binding digests, encoded grant, and
encoded allocation; the engine-owned waiting signal while the attached
child is nonterminal; and, on re-entry with a settled child, output
decoding, projectResult, and ONE atomic settlement join carrying the
conservative accounting summary. Failed children join as the bounded
SubagentExecutionFailure; no in-process child fiber ever starts.
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.
run, stream, and start require LanguageModel.LanguageModel, Model.ProviderName, and
Model.ModelName. Native Effect model Layers provide all three; their client requirements
remain visible until the application supplies them. Supply tool handlers and history alongside
those clients. Keep the model Layer around both start and the detached run’s lifetime.
Subagent.layer captures the supplied model when its handler Layer is built. Use
Layer.provideMerge(ModelLive) when an assembled Layer should expose that model to the parent
too; the attached-subagent walkthrough shows the complete setup.
AutoModel satisfies the same requirement and selects
once on each thread’s first turn, including each new child.
The model Layer must have no construction error. Put fallible setup in the enclosing Layer or
Effect. When constructing a service that needs to reuse a model, capture its client dependencies:
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to use
Use when you want to write effectful code that looks and behaves like
synchronous code, while still handling asynchronous tasks, errors, and complex
control flow such as loops and conditions.
Generator functions work similarly to async/await but keep errors,
requirements, and interruption in the Effect type. You can yield* values
from effects and return the final result at the end.
Provides dependencies to an effect using layers or a context. Use options.local
to build the layer every time; by default, layers are shared between provide
calls.
Selecting between several definitions produces the union of their errors and requirements.
Provide every branch or narrow the selection before execution. Optional explicit bindings are
covered in the API reference.
Keep Thread identities independent of deployment fingerprints. The original admission, input,
principal, digests, idempotency key and Receipt remain immutable. Register one current executable
per stable agentId and optionally declare each Tool’s replay version:
constregistration={
agent:definition,
model:modelLayer,
definitions:deploymentDefinitions,
continuity:{
versions:{
tools:{search:"search-command"},
},
},
};
When continuity.versions is supplied, include every current Tool. Each version is a JSON value;
change it when handler meaning, durable Step codecs or names, external idempotency keys,
authorization semantics, completion projection, or child/report protocols change. Registration hashes
that version with the Tool’s schemas, failure mode, approval policy, execution class, kind, and
completion or context-rollover role. Without explicit
versions, it uses the Tool definitions declaration as the version. JSON Schema cannot detect
changed handler code or codec transforms, so keep those declarations accurate.
Queued and resumed work selects the current binding by agentId. Historical Agent or toolbox
digests do not gate execution. A committed continuation retains its original input and prompt;
static instructions do not require a future input codec to accept that value. Input-dependent
instructions still decode their required input. An incompatible current input Schema returns
BindingUnavailable and leaves the original Receipt owed. Admission, lineage, and prepared
delivery evidence remain exact and are never rewritten.
Recovery checks each pending operation against its recorded execution contract. A changed or
removed mutating Tool that was never dispatched receives ToolUnavailable with
execution: "not-executed", allowing the model to continue with current Tools. If an effect may
have occurred, the call stays unknown unless reconciliation supplies proof. Absence of a prepared
record does not prove that readonly work never ran. See
operation recovery.
Hosts with deferred bindings can compile current metadata without acquiring executable services:
The Effect returns { digests } and requires Crypto. Use those digests in the deferred binding.
There is no historical manifest or Agent replay-version registry to maintain.
For a low-level binding without per-operation hashes, the Tool digest supplies the operation
version. Direct processThread execution without a current registration uses deploymentId
instead. Reusing that ID asserts unchanged handler, Durable Step, and idempotency semantics;
unfinished mutations crossing deployments need registered per-operation versions or reconciliation.
Cloudflare maintenance persists bounded per-lane retries for missing current bindings and
continues other eligible lanes. Reinstantiation retains the backoff; registering the missing Agent
resumes the same Receipt. Parked unknown operations do not block later input in their Thread.
Durable maxDuration bounds each active Attempt using its original recorded allowance. Time
spent evicted, waiting for a binding, or suspended does not consume execution time. An actual
duration exhaustion is recorded before child cleanup and survives recovery. The Run’s start time
and cumulative turn, Tool and cost accounting are unchanged; immutable worker authorization
expiry still limits execution. Roll out the matching runtime and storage packages together before
writing the new duration record; older runtimes cannot read it.
The main operations accept Agent.EncodedInput<typeof definition>. The runtime decodes that value
before instructions run. For Schema.NumberFromString, callers pass a string and instructions
receive a number.
Encode a decoded value with Schema.encodeEffect(definition.input). Use runUnknown,
streamUnknown, or startUnknown for untrusted external data. Invalid input fails with
AgentInputDecodeError before instructions or model execution.
Set completion when a successful tool result should become the agent’s output without another
model turn. The projector receives decoded tool parameters and result:
import{
importAgent
Agent }from"@yielded/agent";
import{
importEffect
Effect,
importSchema
Schema }from"effect";
import{
importTool
Tool,
importToolkit
Toolkit }from"effect/ai";
const
constAnswer:Schema.Struct<{
readonlyanswer:Schema.String;
}>
Answer=
importSchema
Schema.
functionStruct<{
readonlyanswer:Schema.String;
}>(fields:{
readonlyanswer:Schema.String;
}):Schema.Struct<{
readonlyanswer:Schema.String;
}>
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.
Creates a user-defined tool with the specified name and configuration.
Details
This is the primary constructor for creating custom tools that AI models
can call. The tool definition includes parameter validation, success/failure
schemas, and optional service dependencies.
If a tool accepts no parameters but still needs an explicit empty object
schema, use
EmptyParams
.
Example (Creating a tool without parameters)
import{ Schema }from"effect"
import{ Tool }from"effect/ai"
// Simple tool with no parameters
constGetCurrentTime=Tool.make("GetCurrentTime",{
description:"Returns the current timestamp",
success:Schema.Number
})
GetCurrentTime.name// => "GetCurrentTime"
@stability ― unstable
@category ― constructors
@since ― 4.0.0
make("complete",{
parameters?:Schema.Struct<{
readonlyanswer:Schema.String;
}>|undefined
Schema defining the parameters this tool accepts.
parameters:
constAnswer:Schema.Struct<{
readonlyanswer:Schema.String;
}>
Answer,
success?:Schema.Void|undefined
Schema for successful tool execution results.
success:
importSchema
Schema.
constVoid:Schema.Void
Type-level representation of
Void
.
Schema for a TypeScript void return value.
When to use
Use when you need to model the return value of a function, RPC, or endpoint
whose result is intentionally ignored.
Details
Runtime parsing accepts any present value and discards it, producing
undefined. The public decoded and encoded TypeScript representation remains
void, so typed construction, decoding, and encoding APIs are still modeled
as void.
@category ― models
@since ― 3.10.0
@see ― Undefined for a schema that matches only the exact undefined value.
@category ― schemas
@since ― 3.10.0
Void});
const
constTools:Toolkit.Toolkit<{
readonlycomplete:Tool.Tool<"complete",{
readonlyparameters:Schema.Struct<{
readonlyanswer:Schema.String;
}>;
readonlysuccess:Schema.Void;
readonlyfailure:Schema.Never;
readonlyfailureMode:"error";
},never>;
}>
Tools=
importToolkit
Toolkit.
constmake:<[Tool.Tool<"complete",{
readonlyparameters:Schema.Struct<{
readonlyanswer:Schema.String;
}>;
readonlysuccess:Schema.Void;
readonlyfailure:Schema.Never;
readonlyfailureMode:"error";
},never>]>(tools_0:Tool.Tool<"complete",{
readonlyparameters:Schema.Struct<{
readonlyanswer:Schema.String;
}>;
readonlysuccess:Schema.Void;
readonlyfailure:Schema.Never;
readonlyfailureMode:"error";
},never>)=>Toolkit.Toolkit<{
readonlycomplete:Tool.Tool<"complete",{
readonlyparameters:Schema.Struct<{
readonlyanswer:Schema.String;
}>;
readonlysuccess:Schema.Void;
readonlyfailure:Schema.Never;
readonlyfailureMode:"error";
},never>;
}>
Creates a new toolkit from the specified tools.
Details
This is the primary constructor for creating toolkits. It accepts multiple
tools and organizes them into a toolkit that can be provided to AI language
models.
Validate an agent ID and return a shallowly frozen, model-agnostic definition.
make("answer-question",{
DefinitionOptions<Struct<{ readonlyquestion:String; }>,Struct<{ readonlyanswer:String; }>,"Answer the question using the complete tool.",Toolkit<{ readonlycomplete:Tool<...>; }>,undefined,undefined,undefined>.input: Schema.Struct<{
readonlyquestion:Schema.String;
}>
input:
importSchema
Schema.
functionStruct<{
readonlyquestion:Schema.String;
}>(fields:{
readonlyquestion:Schema.String;
}):Schema.Struct<{
readonlyquestion:Schema.String;
}>
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.
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<Struct<{ readonlyquestion:String; }>,Struct<{ readonlyanswer:String; }>,"Answer the question using the complete tool.",Toolkit<{ readonlycomplete:Tool<...>; }>,undefined,undefined,undefined>.output: Schema.Struct<{
readonlyanswer:Schema.String;
}>
output:
constAnswer:Schema.Struct<{
readonlyanswer:Schema.String;
}>
Answer,
DefinitionOptions<Struct<{ readonlyquestion:String; }>,Struct<{ readonlyanswer:String; }>,"Answer the question using the complete tool.",Toolkit<...>,undefined,undefined,undefined>.instructions: "Answer the question using the complete tool."
instructions:"Answer the question using the complete tool.",
DefinitionOptions<Struct<{ readonlyquestion:String; }>,Struct<{ readonlyanswer:String; }>,"Answer the question using the complete tool.",Toolkit<{ readonlycomplete:Tool<...>; }>,undefined,undefined,undefined>.toolkit: Toolkit.Toolkit<{
readonlycomplete:Tool.Tool<"complete",{
readonlyparameters:Schema.Struct<{
readonlyanswer:Schema.String;
}>;
readonlysuccess:Schema.Void;
readonlyfailure:Schema.Never;
readonlyfailureMode:"error";
},never>;
}>
toolkit:
constTools:Toolkit.Toolkit<{
readonlycomplete:Tool.Tool<"complete",{
readonlyparameters:Schema.Struct<{
readonlyanswer:Schema.String;
}>;
readonlysuccess:Schema.Void;
readonlyfailure:Schema.Never;
readonlyfailureMode:"error";
},never>;
}>
Tools,
DefinitionOptions<Struct<{ readonlyquestion:String; }>,Struct<{ readonlyanswer:String; }>,"Answer the question using the complete tool.",Toolkit<{ readonlycomplete:Tool<...>; }>,undefined,undefined,undefined>.policy?:Partial<Readonly<Omit<{
readonlyrunStatus:"appended"|"off";
readonlymaxTurns:number;
readonlymaxToolCalls:number;
readonlymaxDuration:Duration;
readonlytoolConcurrency:number;
readonlyrepeatedFailureLimit:number;
readonlyonExhaustion:"final-answer"|"fail";
readonlycompletionReserveTokens:number;
readonlytoolResultBounds:ToolResultBounds;
readonlycompaction:CompactionPolicy;
readonlytokenBudget?:number|undefined;
readonlycostBudgetMicrousd?:number|undefined;
readonlycontextTokenLimit?:number|undefined;
readonlyrestartOnJoinedInput?:boolean|undefined;
readonlymodelRetries?:number|undefined;
},"runStatus"| ... 5more... |"compaction">&{
...;
}>>|undefined
policy:{
maxTurns?:number|undefined
maxTurns:3,
maxToolCalls?:number|undefined
maxToolCalls:3,
maxDuration?:Input|undefined
Finite, positive wall-clock duration accepted in any Effect Duration input form.
maxDuration:"30 seconds",
},
DefinitionOptions<Struct<{ readonlyquestion:String; }>,Struct<{ readonlyanswer:String; }>,"Answer the question using the complete tool.",Toolkit<...>,undefined,undefined,undefined>.completion?:(Agent.CompletionToolDeclaration<{
Converts a toolkit into a Layer containing handlers for each tool in the
toolkit.
toLayer({
complete:()=>Effect.Effect<void,never,never>
complete:()=>
importEffect
Effect.
constvoid:Effect.Effect<void,never,never>
exportvoid
Returns an effect that succeeds with void.
@category ― constructors
@since ― 2.0.0
void});
Provide ToolsLive with the model and history services when running this definition.
The output schema validates the projected value. With required: true, each turn must call a
tool and completion must use the named tool. Without it, valid final assistant JSON may also
complete the run. Exhaustion policy
controls the last available turn.
A completion tool must be the only application call, after any other needed application tool
results arrive. Completed provider work, such as hosted web search with a terminal result, may
appear in the same response and still counts toward run budgets. When a model mixes a completion
tool with other application calls, the engine rejects those application calls before any handler
starts and returns one ModelProtocolError result per rejected call. Provider results are retained.
The next ordinary turn can correct the declaration. Each rejected call counts toward maxToolCalls and
repeatedFailureLimit; model usage, turns, cost, and duration remain charged to the same run.
There is no separate retry allowance. A batch of three rejected calls can therefore exhaust the
default repeated-failure limit immediately. Budget exhaustion still controls finalization, and an
invalid finalization batch fails without another correction.
This correction also applies to completionFromTools. Provider calls without terminal results,
context rollover mixed with other calls, and invalid application batches recovered as pending work
still fail: pending durable calls cannot safely be treated as unexecuted. Recovery permits one
completion call alongside recorded terminal provider results, using recorded application results
when available and never replaying an uncertain ordinary side effect. Finalization after budget
exhaustion still permits only the advertised completion tool, with no provider calls. Completed
rejections are retained atomically with their failed results and survive recovery without replay.
Ordinary valid tool batches keep their configured concurrency. ToolCallFailed events identify
each rejected call by name and ID; turn events show correction attempts and the terminal run event
reports their outcome.
completion decides how a run finishes. runDisposition labels its successful output for
durable readers.
Use completionFromTools for an action whose committed result can satisfy the whole request,
while retaining a separate completion tool for ordinary replies. Each declaration names a tool
and provides a pure projector returning Option.some(output) or Option.none():
Import Option from effect. The tool’s schemas define the parameters and result in this example.
Only opt in tools with an explicit whole-request contract; completing the first step of a larger
request must not end the run. Return None for pending approval, partial, or otherwise insufficient
results. A declared tool failure also continues normally. Invalid output or a throwing projector
fails the run, rather than treating an unconfirmed result as success.
These action tools must be the only call in their batch and obey ordinary tool, turn, and token
budgets. They cannot execute in the reserved finalization turn; the existing completion tool
retains that role. Authorization, approvals, cancellation, and durable tool replay rules remain
unchanged. Recovery re-evaluates the projector from canonical parameters and results, so it must
be deterministic and perform no effects. Tool names must be distinct across completion declarations.
The selector receives decoded output and may return undefined. The schema validates and encodes
any returned value. Invalid output fails with AgentRunDispositionError.
Only an ordinary completed durable run stores the encoded disposition on
SubmissionSettled.runDisposition. Budget exhaustion, failure, abort, and incomplete recovery
store none. Parse it with the same application schema. Do not infer completion from prose or tool
success.
The string passed to Agent.make becomes a branded AgentId and contributes to durable identity
and definition digests. Renaming it creates a new identity. Before 1.0, stored data has no
migration promise.
Normalize and validate finite policy bounds, throwing on invalid input.
make({
maxTurns:number
maxTurns:12,
maxToolCalls:number
maxToolCalls:24,
maxDuration:Input
Finite, positive wall-clock duration accepted in any Effect Duration input form.
maxDuration:"5 minutes",
toolConcurrency:number
toolConcurrency:4,
repeatedFailureLimit?:number|undefined
Non-negative repeated-failure bound; defaults to 3.
repeatedFailureLimit:3,
tokenBudget?:number|undefined
tokenBudget:80_000,
costBudgetMicrousd?:number|undefined
costBudgetMicrousd:2_000_000,
onExhaustion?:"final-answer"|"fail"|undefined
Turn/Tool-Call/token exhaustion resolution; defaults to "final-answer".
onExhaustion:"final-answer",
});
Turns, tool calls, duration, and concurrency need positive finite bounds. Token and cost budgets
are optional because some models do not report enough usage data.
The default onExhaustion: "final-answer" allows one constrained final answer for turn, tool call,
or token exhaustion. The result uses finishReason: "budget-exhausted". See
Budgets & bounded autonomy for all exhaustion rules and
Context management for tool result bounds, run status, context limits,
and compaction.