Skip to content

Subagents

Durable background subagents

Give the parent tools to start and steer a researcher while it keeps chatting:

import {
import Subagent
Subagent
} from "@yielded/agent";
import {
const Researcher: Definition<Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, Toolkit<{
readonly search_activities: Tool<"search_activities", {
readonly parameters: Struct<{
readonly city: String;
}>;
readonly success: $Array<String>;
readonly failure: Never;
readonly failureMode: "error";
}, never>;
}>, undefined, undefined, undefined> & {
...;
}
Researcher
} from "./researcher.ts";
const
const background: Readonly<{
tools: Subagent.BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>;
toolkit: Toolkit<Subagent.BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<...>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>;
layer: Layer<...>;
}>
background
=
import Subagent
Subagent
.
function background<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, {
readonly search_activities: Tool<"search_activities", {
readonly parameters: Struct<{
readonly city: String;
}>;
readonly success: $Array<String>;
readonly failure: Never;
readonly failureMode: "error";
}, never>;
}, {
...;
}>(target: Definition<...> & {
...;
}, selected: {
...;
}): Readonly<...> (+1 overload)

Derive selected background Tools directly from a child Agent, using its ID as the delegation name and its input/output Schemas as the default contract. Pass an explicit Subagent.make declaration to customize the name, projections, grants, or policy bounds.

background
(
const Researcher: Definition<Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, Toolkit<{
readonly search_activities: Tool<"search_activities", {
readonly parameters: Struct<{
readonly city: String;
}>;
readonly success: $Array<String>;
readonly failure: Never;
readonly failureMode: "error";
}, never>;
}>, undefined, undefined, undefined> & {
...;
}
Researcher
, {
start: true
start
: true,
followUp: true
followUp
: true,
reportToParent: true
reportToParent
: true,
});

A start returns { worker, delivery }. The delivery’s message reference identifies the retained input; its receipt appears after destination acceptance. A pending delivery is queued for delivery and says nothing about child execution. The parent keeps responding, and a WorkerCompletion message arrives when the child run ends. It contains the projected result or a bounded failure, the worker and run identities, and a budget-exhaustion flag. Finishing or aborting the parent run leaves the worker and pending report running.

Reports join an active parent run at an input boundary or start a later run in the same thread. The framework delivers them separately from the parent’s application input: no report tags, mapper, input union, or extra host registration is required. Existing callers must opt in.

Opt a worker definition into assignment completion through its typed output:

import {
import Agent
Agent
,
import Worker
Worker
} from "@yielded/agent";
import {
import Schema
Schema
} from "effect";
import {
import Toolkit
Toolkit
} from "effect/ai";
const
const Task: Agent.Definition<Schema.String, Schema.Struct<{
readonly status: Schema.Literals<readonly ["completed", "waiting"]>;
readonly answer: Schema.String;
}>, "Use waiting when you need an answer; completed only when the assignment is done.", Toolkit.Toolkit<{}>, Agent.RunDispositionDeclaration<{
readonly status: "completed" | "waiting";
readonly answer: string;
}, Schema.Literals<readonly ["completed", "waiting"]>>, undefined, undefined> & {
readonly id: Brand<"@effect-agent/core/AgentId"> & "task";
}
Task
=
import Agent
Agent
.
function make<"task", Schema.String, Schema.Struct<{
readonly status: Schema.Literals<readonly ["completed", "waiting"]>;
readonly answer: Schema.String;
}>, "Use waiting when you need an answer; completed only when the assignment is done.", Toolkit.Toolkit<{}>, Schema.Literals<readonly ["completed", "waiting"]>, undefined>(id: "task", options: Agent.DefinitionOptions<Schema.String, Schema.Struct<{
readonly status: Schema.Literals<readonly ["completed", "waiting"]>;
readonly answer: Schema.String;
}>, "Use waiting when you need an answer; completed only when the assignment is done.", Toolkit.Toolkit<...>, Agent.RunDispositionDeclaration<...>, undefined, undefined> & {
...;
}): Agent.Definition<...> & {
...;
} (+3 overloads)

Validate an agent ID and return a shallowly frozen, model-agnostic definition.

make
("task", {
DefinitionOptions<String, Struct<{ readonly status: Literals<readonly ["completed", "waiting"]>; readonly answer: String; }>, "Use waiting when you need an answer; completed only when the assignment is done.", Toolkit<...>, RunDispositionDeclaration<...>, undefined, undefined>.input: Schema.String
input
:
import Schema
Schema
.
const String: 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<{ readonly status: Literals<readonly ["completed", "waiting"]>; readonly answer: String; }>, "Use waiting when you need an answer; completed only when the assignment is done.", Toolkit<...>, RunDispositionDeclaration<...>, undefined, undefined>.output: Schema.Struct<{
readonly status: Schema.Literals<readonly ["completed", "waiting"]>;
readonly answer: Schema.String;
}>
output
:
import Schema
Schema
.
function Struct<{
readonly status: Schema.Literals<readonly ["completed", "waiting"]>;
readonly answer: Schema.String;
}>(fields: {
readonly status: Schema.Literals<readonly ["completed", "waiting"]>;
readonly answer: Schema.String;
}): Schema.Struct<{
readonly status: Schema.Literals<readonly ["completed", "waiting"]>;
readonly answer: 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.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@category ― constructors

@since ― 3.10.0

Struct
({
status: Schema.Literals<readonly ["completed", "waiting"]>
status
:
import Worker
Worker
.
const AssignmentDisposition: Schema.Literals<readonly ["completed", "waiting"]>

Assignment output selected through the Definition's runDisposition declaration.

AssignmentDisposition
,
answer: Schema.String
answer
:
import Schema
Schema
.
const String: 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<{ readonly status: Literals<readonly ["completed", "waiting"]>; readonly answer: String; }>, "Use waiting when you need an answer; completed only when the assignment is done.", Toolkit<...>, RunDispositionDeclaration<...>, undefined, undefined>.instructions: "Use waiting when you need an answer; completed only when the assignment is done."
instructions
: "Use waiting when you need an answer; completed only when the assignment is done.",
DefinitionOptions<String, Struct<{ readonly status: Literals<readonly ["completed", "waiting"]>; readonly answer: String; }>, "Use waiting when you need an answer; completed only when the assignment is done.", Toolkit<...>, RunDispositionDeclaration<...>, undefined, undefined>.toolkit: Toolkit.Toolkit<{}>
toolkit
:
import Toolkit
Toolkit
.
const empty: 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.

@stability ― unstable

@category ― constructors

@since ― 4.0.0

empty
,
runDisposition: Agent.RunDispositionDeclaration<{
readonly status: "completed" | "waiting";
readonly answer: string;
}, Schema.Literals<readonly ["completed", "waiting"]>>
runDisposition
: {
RunDispositionDeclaration<Output, DispositionSchema extends Schema.Top>.workerLifecycle?: "assignment" | undefined

Opt background workers into one retained assignment. The encoded disposition must be Worker.AssignmentDisposition: completed seals the worker, waiting leaves it steerable. Failures and exhausted Runs seal as failed; aborting an active Run seals as cancelled. The worker origin retains this choice. Omission preserves reusable workers.

workerLifecycle
: "assignment",
RunDispositionDeclaration<{ readonly status: "completed" | "waiting"; readonly answer: string; }, Literals<readonly ["completed", "waiting"]>>.schema: Schema.Literals<readonly ["completed", "waiting"]>

Canonical Schema used to validate and encode the selected disposition.

schema
:
import Worker
Worker
.
const AssignmentDisposition: Schema.Literals<readonly ["completed", "waiting"]>

Assignment output selected through the Definition's runDisposition declaration.

AssignmentDisposition
,
RunDispositionDeclaration<{ readonly status: "completed" | "waiting"; readonly answer: string; }, Literals<readonly ["completed", "waiting"]>>.fromOutput: (output: {
readonly status: "completed" | "waiting";
readonly answer: string;
}) => unknown

Pure selection from decoded output. undefined declares none, except for assignments.

fromOutput
: (
output: {
readonly status: "completed" | "waiting";
readonly answer: string;
}
output
) =>
output: {
readonly status: "completed" | "waiting";
readonly answer: string;
}
output
.
status: "completed" | "waiting"
status
,
},
});

Start and steer Task through the same background APIs. A waiting result ends that run and keeps the assignment steerable. A completed result permanently seals the assignment only after its latest accepted instructions have been applied. Failed or exhausted runs also seal it. Inspect the worker’s state to distinguish assignment completion from a completed run. The choice is retained at worker creation; existing workers and definitions without this opt-in remain reusable. See the terminal contract.

Declare the update Schema on the Agent once, then enable parent reporting:

background-updates.ts
import {
import Agent
Agent
,
import Subagent
Subagent
} from "@yielded/agent";
import {
import Schema
Schema
} from "effect";
import {
import Toolkit
Toolkit
} from "effect/ai";
export const
const HotelRequest: Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>
HotelRequest
=
import Schema
Schema
.
function Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>(fields: {
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}): Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: 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.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@category ― constructors

@since ― 3.10.0

Struct
({
city: Schema.String
city
:
import Schema
Schema
.
const String: 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
,
area: Schema.String
area
:
import Schema
Schema
.
const String: 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
,
sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>
sources
:
import Schema
Schema
.
Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>(self: Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>): Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>
export Array

Defines a ReadonlyArray schema for a given element schema.

Example (Defining an array of strings)

import { Schema } from "effect"
const schema = Schema.Array(Schema.String)
Schema.decodeUnknownSync(schema)(["a", "b", "c"]) // => ["a", "b", "c"]

@category ― constructors

@since ― 4.0.0

Array
(
import Schema
Schema
.
function Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>(fields: {
readonly url: Schema.String;
readonly notes: Schema.String;
}): Schema.Struct<{
readonly url: Schema.String;
readonly notes: 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.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@category ― constructors

@since ― 3.10.0

Struct
({
url: Schema.String
url
:
import Schema
Schema
.
const String: 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
,
notes: Schema.String
notes
:
import Schema
Schema
.
const String: 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
})),
});
export const
const AreaConcern: Schema.TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<Schema.String>;
}>
AreaConcern
=
import Schema
Schema
.
function TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<Schema.String>;
}>(value: "AreaConcern", fields: {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<Schema.String>;
}): Schema.TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<Schema.String>;
}>

Creates a struct schema with an automatically populated _tag field.

When to use

Use to define a tagged union case from a literal tag and a set of fields.

Details

When using the make method, the _tag field is optional and will be added automatically. However, when decoding or encoding, the _tag field must be present in the input.

Example (Defining a tagged struct shorthand)

import { Schema } from "effect"
// Defines a struct with a fixed `_tag` field
const tagged = Schema.TaggedStruct("A", {
a: Schema.String
})
// This is the same as writing:
const equivalent = Schema.Struct({
_tag: Schema.tag("A"),
a: Schema.String
})
void tagged
void equivalent

Example (Accessing the literal value of the tag)

import { Schema } from "effect"
const tagged = Schema.TaggedStruct("A", {
a: Schema.String
})
tagged.fields._tag.schema.literal // => "A"

@category ― constructors

@since ― 3.10.0

TaggedStruct
("AreaConcern", {
area: Schema.String
area
:
import Schema
Schema
.
const String: 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
,
finding: Schema.String
finding
:
import Schema
Schema
.
const String: 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
,
sources: Schema.$Array<Schema.String>
sources
:
import Schema
Schema
.
Array<Schema.String>(self: Schema.String): Schema.$Array<Schema.String>
export Array

Defines a ReadonlyArray schema for a given element schema.

Example (Defining an array of strings)

import { Schema } from "effect"
const schema = Schema.Array(Schema.String)
Schema.decodeUnknownSync(schema)(["a", "b", "c"]) // => ["a", "b", "c"]

@category ― constructors

@since ― 4.0.0

Array
(
import Schema
Schema
.
const String: 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
),
});
export const
const HotelResearcher: Agent.Definition<Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>, Schema.Struct<{
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: Schema.String;
}>, string, Toolkit.Toolkit<{
readonly emit_update: Tool<"emit_update", {
readonly parameters: Schema.Struct<{
readonly value: NoInfer<Schema.TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<...>;
}>>;
}>;
readonly success: Schema.Struct<...>;
readonly failure: typeof UpdateError;
readonly failureMode: "return";
}, Emitter>;
}>, undefined, undefined, NoInfer<Schema.TaggedStruct<...>>> & {
...;
}
HotelResearcher
=
import Agent
Agent
.
function make<"hotel-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>, Schema.Struct<{
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: Schema.String;
}>, string, Toolkit.Toolkit<{}>, Schema.TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<...>;
}>>(id: "hotel-researcher", options: Agent.DefinitionOptions<...> & {
...;
}): Agent.Definition<...> & {
...;
} (+3 overloads)

Validate an agent ID and return a shallowly frozen, model-agnostic definition.

make
("hotel-researcher", {
DefinitionOptions<Struct<{ readonly city: String; readonly area: String; readonly sources: $Array<Struct<{ readonly url: String; readonly notes: String; }>>; }>, ... 5 more ..., TaggedStruct<...>>.input: Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>
input
:
const HotelRequest: Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>
HotelRequest
,
DefinitionOptions<Struct<{ readonly city: String; readonly area: String; readonly sources: $Array<Struct<{ readonly url: String; readonly notes: String; }>>; }>, ... 5 more ..., TaggedStruct<...>>.updates?: Schema.TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<Schema.String>;
}> | undefined
updates
:
const AreaConcern: Schema.TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<Schema.String>;
}>
AreaConcern
,
DefinitionOptions<Struct<{ readonly city: String; readonly area: String; readonly sources: $Array<Struct<{ readonly url: String; readonly notes: String; }>>; }>, ... 5 more ..., TaggedStruct<...>>.output: Schema.Struct<{
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: Schema.String;
}>
output
:
import Schema
Schema
.
function Struct<{
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: Schema.String;
}>(fields: {
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: Schema.String;
}): Schema.Struct<{
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: 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.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@category ― constructors

@since ― 3.10.0

Struct
({
hotels: Schema.$Array<Schema.String>
hotels
:
import Schema
Schema
.
Array<Schema.String>(self: Schema.String): Schema.$Array<Schema.String>
export Array

Defines a ReadonlyArray schema for a given element schema.

Example (Defining an array of strings)

import { Schema } from "effect"
const schema = Schema.Array(Schema.String)
Schema.decodeUnknownSync(schema)(["a", "b", "c"]) // => ["a", "b", "c"]

@category ― constructors

@since ― 4.0.0

Array
(
import Schema
Schema
.
const String: 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
),
summary: Schema.String
summary
:
import Schema
Schema
.
const String: 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<Struct<{ readonly city: String; readonly area: String; readonly sources: $Array<Struct<{ readonly url: String; readonly notes: String; }>>; }>, ... 5 more ..., TaggedStruct<...>>.instructions: string
instructions
:
"Review the supplied source notes for hotel options. Use emit_update to share a material " +
"area concern as soon as you find one, citing only supplied sources. Continue the research " +
"after emitting. Treat concerns as provisional and incorporate follow-up preferences.",
DefinitionOptions<Struct<{ readonly city: String; readonly area: String; readonly sources: $Array<Struct<{ readonly url: String; readonly notes: String; }>>; }>, ... 5 more ..., TaggedStruct<...>>.toolkit: Toolkit.Toolkit<{}>
toolkit
:
import Toolkit
Toolkit
.
const empty: 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.

@stability ― unstable

@category ― constructors

@since ― 4.0.0

empty
,
DefinitionOptions<Struct<{ readonly city: String; readonly area: String; readonly sources: $Array<Struct<{ readonly url: String; readonly notes: String; }>>; }>, ... 5 more ..., TaggedStruct<...>>.policy?: Partial<Readonly<Omit<{
readonly runStatus: "appended" | "off";
readonly maxTurns: number;
readonly maxToolCalls: number;
readonly maxDuration: Duration;
readonly toolConcurrency: number;
readonly repeatedFailureLimit: number;
readonly onExhaustion: "final-answer" | "fail";
readonly completionReserveTokens: number;
readonly toolResultBounds: ToolResultBounds;
readonly compaction: CompactionPolicy;
readonly tokenBudget?: number | undefined;
readonly costBudgetMicrousd?: number | undefined;
readonly contextTokenLimit?: number | undefined;
readonly restartOnJoinedInput?: boolean | undefined;
readonly modelRetries?: number | undefined;
}, "runStatus" | ... 5 more ... | "compaction"> & {
...;
}>> | undefined
policy
: {
maxTurns?: number | undefined
maxTurns
: 6,
maxToolCalls?: number | undefined
maxToolCalls
: 4,
maxDuration?: Input | undefined

Finite, positive wall-clock duration accepted in any Effect Duration input form.

maxDuration
: "2 minutes" },
});
export const
const hotels: Readonly<{
tools: Subagent.BackgroundTools<"hotel-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>, Schema.Struct<{
readonly output: Schema.Struct<{
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>;
toolkit: Toolkit.Toolkit<...>;
layer: Layer<...>;
}>
hotels
=
import Subagent
Subagent
.
function background<"hotel-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>, Schema.Struct<{
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: Schema.String;
}>, string, {
readonly emit_update: Tool<"emit_update", {
readonly parameters: Schema.Struct<{
readonly value: NoInfer<Schema.TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<...>;
}>>;
}>;
readonly success: Schema.Struct<...>;
readonly failure: typeof UpdateError;
readonly failureMode: "return";
}, Emitter>;
}, {
...;
}>(target: Agent.Definition<...> & {
...;
}, selected: {
...;
}): Readonly<...> (+1 overload)

Derive selected background Tools directly from a child Agent, using its ID as the delegation name and its input/output Schemas as the default contract. Pass an explicit Subagent.make declaration to customize the name, projections, grants, or policy bounds.

background
(
const HotelResearcher: Agent.Definition<Schema.Struct<{
readonly city: Schema.String;
readonly area: Schema.String;
readonly sources: Schema.$Array<Schema.Struct<{
readonly url: Schema.String;
readonly notes: Schema.String;
}>>;
}>, Schema.Struct<{
readonly hotels: Schema.$Array<Schema.String>;
readonly summary: Schema.String;
}>, string, Toolkit.Toolkit<{
readonly emit_update: Tool<"emit_update", {
readonly parameters: Schema.Struct<{
readonly value: NoInfer<Schema.TaggedStruct<"AreaConcern", {
readonly area: Schema.String;
readonly finding: Schema.String;
readonly sources: Schema.$Array<...>;
}>>;
}>;
readonly success: Schema.Struct<...>;
readonly failure: typeof UpdateError;
readonly failureMode: "return";
}, Emitter>;
}>, undefined, undefined, NoInfer<Schema.TaggedStruct<...>>> & {
...;
}
HotelResearcher
, {
start: true
start
: true,
followUp: true
followUp
: true,
reportToParent: true
reportToParent
: true,
});

Save as background-updates.ts. This example reviews source notes supplied in its input; add your research tools to its toolkit for live retrieval. The native emit_update tool accepts { value: AreaConcern }. Its acknowledgement retains the finding and lets the child continue. An update is provisional information, independent of the final hotel result.

Give a coordinator hotels.toolkit and provide hotels.layer. Register the exact HotelResearcher definition alongside that coordinator, using the host setup below. With reportToParent: true, the parent receives both WorkerUpdate and WorkerCompletion without an application input union, mapper, or reporting entry. Agents without updates continue to send only completion.

The parent consumes the finding at a safe input boundary or in a later run. It can explain the concern, ask the user how to proceed, and use follow-up tools to redirect the hotel worker and other workers to Rosebank. Emission does not wait for a user decision or stop the child. See update delivery guarantees for ordering, backpressure, and recovery.

background-coordinator.ts
import {
import Subagent
Subagent
,
import Agent
Agent
} from "@yielded/agent";
import {
import Schema
Schema
} from "effect";
import {
const CoordinatorInput: Schema.Struct<{
readonly text: Schema.String;
}>
CoordinatorInput
} from "./background-input.ts";
import {
const Researcher: Agent.Definition<Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, Toolkit<{
readonly search_activities: Tool<"search_activities", {
readonly parameters: Schema.Struct<{
readonly city: Schema.String;
}>;
readonly success: Schema.$Array<Schema.String>;
readonly failure: Schema.Never;
readonly failureMode: "error";
}, never>;
}>, undefined, undefined, undefined> & {
...;
}
Researcher
} from "./researcher.ts";
export const
const ResearchBackground: Readonly<{
tools: Subagent.BackgroundTools<"activity-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>;
toolkit: Toolkit<Subagent.BackgroundTools<"activity-researcher", Schema.Struct<...>, Schema.Struct<...>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>;
layer: Layer<...>;
}>
ResearchBackground
=
import Subagent
Subagent
.
function background<"activity-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, {
readonly search_activities: Tool<"search_activities", {
readonly parameters: Schema.Struct<{
readonly city: Schema.String;
}>;
readonly success: Schema.$Array<Schema.String>;
readonly failure: Schema.Never;
readonly failureMode: "error";
}, never>;
}, {
...;
}>(target: Agent.Definition<...> & {
...;
}, selected: {
...;
}): Readonly<...> (+1 overload)

Derive selected background Tools directly from a child Agent, using its ID as the delegation name and its input/output Schemas as the default contract. Pass an explicit Subagent.make declaration to customize the name, projections, grants, or policy bounds.

background
(
const Researcher: Agent.Definition<Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, Toolkit<{
readonly search_activities: Tool<"search_activities", {
readonly parameters: Schema.Struct<{
readonly city: Schema.String;
}>;
readonly success: Schema.$Array<Schema.String>;
readonly failure: Schema.Never;
readonly failureMode: "error";
}, never>;
}>, undefined, undefined, undefined> & {
...;
}
Researcher
, {
start: true
start
: true,
followUp: true
followUp
: true,
reportToParent: true
reportToParent
: true,
});
export const
const BackgroundCoordinator: Agent.Definition<Schema.Struct<{
readonly text: Schema.String;
}>, Schema.String, string, Toolkit<Subagent.BackgroundTools<"activity-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>, undefined, undefined, undefined> & {
...;
}
BackgroundCoordinator
=
import Agent
Agent
.
function make<"background-trip-coordinator", Schema.Struct<{
readonly text: Schema.String;
}>, Schema.String, string, Toolkit<Subagent.BackgroundTools<"activity-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>, undefined>(id: "background-trip-coordinator", options: Agent.DefinitionOptions<...> & {
...;
}): Agent.Definition<...> & {
...;
} (+3 overloads)

Validate an agent ID and return a shallowly frozen, model-agnostic definition.

make
("background-trip-coordinator", {
DefinitionOptions<Struct<{ readonly text: String; }>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{ readonly city: String; readonly focus: String; }>, Struct<...>, Never, { ...; }>>, undefined, undefined, undefined>.input: Schema.Struct<{
readonly text: Schema.String;
}>
input
:
const CoordinatorInput: Schema.Struct<{
readonly text: Schema.String;
}>
CoordinatorInput
,
DefinitionOptions<Struct<{ readonly text: String; }>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{ readonly city: String; readonly focus: String; }>, Struct<...>, Never, { ...; }>>, undefined, undefined, undefined>.output: Schema.String
output
:
import Schema
Schema
.
const String: 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<Struct<{ readonly text: String; }>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{ readonly city: String; readonly focus: String; }>, Struct<...>, Never, { ...; }>>, undefined, undefined, undefined>.toolkit: Toolkit<Subagent.BackgroundTools<"activity-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>
toolkit
:
const ResearchBackground: Readonly<{
tools: Subagent.BackgroundTools<"activity-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>;
toolkit: Toolkit<Subagent.BackgroundTools<"activity-researcher", Schema.Struct<...>, Schema.Struct<...>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>;
layer: Layer<...>;
}>
ResearchBackground
.
toolkit: Toolkit<Subagent.BackgroundTools<"activity-researcher", Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>, Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>, Schema.Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>
toolkit
,
DefinitionOptions<Struct<{ readonly text: String; }>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{ readonly city: String; readonly focus: String; }>, Struct<...>, Never, { ...; }>>, undefined, undefined, undefined>.instructions: string
instructions
:
"Help the user plan a trip. Start activity research in the background when needed. " +
"Keep discussing their preferences while research runs. Send changed preferences " +
"to the existing worker with follow_up. When WorkerCompletion arrives, explain " +
"the findings and flag partial results. On failure or cancellation, help choose a next step. " +
"Do not start another search just because a research report arrived.",
DefinitionOptions<Struct<{ readonly text: String; }>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{ readonly city: String; readonly focus: String; }>, Struct<...>, Never, { ...; }>>, undefined, undefined, undefined>.policy?: Partial<Readonly<Omit<{
readonly runStatus: "appended" | "off";
readonly maxTurns: number;
readonly maxToolCalls: number;
readonly maxDuration: Duration;
readonly toolConcurrency: number;
readonly repeatedFailureLimit: number;
readonly onExhaustion: "final-answer" | "fail";
readonly completionReserveTokens: number;
readonly toolResultBounds: ToolResultBounds;
readonly compaction: CompactionPolicy;
readonly tokenBudget?: number | undefined;
readonly costBudgetMicrousd?: number | undefined;
readonly contextTokenLimit?: number | undefined;
readonly restartOnJoinedInput?: boolean | undefined;
readonly modelRetries?: number | undefined;
}, "runStatus" | ... 5 more ... | "compaction"> & {
...;
}>> | undefined
policy
: {
maxTurns?: number | undefined
maxTurns
: 6,
maxToolCalls?: number | undefined
maxToolCalls
: 4,
maxDuration?: Input | undefined

Finite, positive wall-clock duration accepted in any Effect Duration input form.

maxDuration
: "2 minutes",
toolConcurrency?: number | undefined
toolConcurrency
: 2 },
});

Save as background-coordinator.ts. This uses the activity researcher directly. The default result is { output, budgetExhausted }. Use an explicit Subagent.make declaration when the parent should receive a custom result projection.

background-input.ts
import {
import Schema
Schema
} from "effect";
export const
const CoordinatorInput: Schema.Struct<{
readonly text: Schema.String;
}>
CoordinatorInput
=
import Schema
Schema
.
function Struct<{
readonly text: Schema.String;
}>(fields: {
readonly text: Schema.String;
}): Schema.Struct<{
readonly text: 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.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@category ― constructors

@since ― 3.10.0

Struct
({
text: Schema.String
text
:
import Schema
Schema
.
const String: 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
});

Save as background-input.ts. Instructions and host policy keep the original admitted application input as their context. For a completion, the framework renders the typed message instead of calling the application’s inputPrompt again.

background-host.ts
import {
import NodeDurableHost
NodeDurableHost
} from "@yielded/agent-platform-node";
import {
import Layer
Layer
} from "effect";
import {
const WorkerAccessLive: Layer.Layer<never, never, never>
WorkerAccessLive
} from "./background-access.ts";
import {
const BackgroundCoordinator: Definition<Struct<{
readonly text: String;
}>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>, undefined, undefined, undefined> & {
...;
}
BackgroundCoordinator
,
const ResearchBackground: Readonly<{
tools: BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>;
toolkit: Toolkit<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<...>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>;
layer: Layer.Layer<...>;
}>
ResearchBackground
} from "./background-coordinator.ts";
import {
const definitions: {
agent: string;
model: string;
tools: string;
}
definitions
,
const ModelLive: Model<"openai", LanguageModel, OpenAiClient>
ModelLive
,
const OpenAiLive: Layer.Layer<OpenAiClient, ConfigError, never>
OpenAiLive
} from "./node-agent.ts";
import {
const Researcher: Definition<Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, Toolkit<{
readonly search_activities: Tool<"search_activities", {
readonly parameters: Struct<{
readonly city: String;
}>;
readonly success: $Array<String>;
readonly failure: Never;
readonly failureMode: "error";
}, never>;
}>, undefined, undefined, undefined> & {
...;
}
Researcher
} from "./researcher.ts";
import {
const TravelToolsLive: Layer.Layer<Handler<"search_activities">, never, never>
TravelToolsLive
} from "./tools.ts";
export const
const HostLive: Layer.Layer<DurableAgentRuntime | MessageDeliveryStore | ThreadReader | NodeDurableAgentRuntimeConfig | (Crypto & DurableAgentRuntime) | (Crypto & MessageDeliveryStore) | (Crypto & ThreadReader) | (Crypto & NodeDurableAgentRuntimeConfig) | (SqlClient & DurableAgentRuntime) | (SqlClient & MessageDeliveryStore) | ... 6 more ... | NodeDurableHost.NodeDurableHost, ConfigError | ... 3 more ... | NodePlatformConfigError, never>
HostLive
=
import NodeDurableHost
NodeDurableHost
.
const layer: <readonly [{
readonly agent: Definition<Struct<{
readonly text: String;
}>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>, undefined, undefined, undefined> & {
...;
};
readonly model: Model<...>;
readonly definitions: {
...;
};
}, {
...;
}], never, never, never, never, never, never>(registrations: readonly [...], options: NodeDurableAgentRuntimeOptions<...>) => Layer.Layer<...>

Acquire a complete Node host and start one bounded, scoped worker pool after recovery. Own the SQLite file exclusively until the host and its storage close. A second connection fails construction; after process death the replacement retires abandoned claims before recovery, without waiting for their leases. Use this host's services for live inspection. Provide model, tool, instruction, and schema dependencies to this Layer. Reusing the Layer shares the same pool. A worker failure closes admission; observe it with run at the process boundary so the application exits and releases the host instead of remaining idle.

layer
(
[
{
agent: Definition<Struct<{
readonly text: String;
}>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>, undefined, undefined, undefined> & {
...;
}
agent
:
const BackgroundCoordinator: Definition<Struct<{
readonly text: String;
}>, String, string, Toolkit<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>, undefined, undefined, undefined> & {
...;
}
BackgroundCoordinator
,
model: Model<"openai", LanguageModel, OpenAiClient>
model
:
const ModelLive: Model<"openai", LanguageModel, OpenAiClient>
ModelLive
,
definitions: {
agent: string;
model: string;
tools: string;
}
definitions
,
},
{
agent: Definition<Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, Toolkit<{
readonly search_activities: Tool<"search_activities", {
readonly parameters: Struct<{
readonly city: String;
}>;
readonly success: $Array<String>;
readonly failure: Never;
readonly failureMode: "error";
}, never>;
}>, undefined, undefined, undefined> & {
...;
}
agent
:
const Researcher: Definition<Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>, ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string, Toolkit<{
readonly search_activities: Tool<"search_activities", {
readonly parameters: Struct<{
readonly city: String;
}>;
readonly success: $Array<String>;
readonly failure: Never;
readonly failureMode: "error";
}, never>;
}>, undefined, undefined, undefined> & {
...;
}
Researcher
,
model: Model<"openai", LanguageModel, OpenAiClient>
model
:
const ModelLive: Model<"openai", LanguageModel, OpenAiClient>
ModelLive
,
definitions: {
agent: string;
model: string;
tools: string;
}
definitions
},
],
{
NodeDurableAgentRuntimeOptions<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never, ReconcilerError = never, ReconcilerRequirements = never>.filename: string
filename
: "./agents.sqlite",
NodeDurableAgentRuntimeOptions<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never, ReconcilerError = never, ReconcilerRequirements = never>.deploymentId: string
deploymentId
: "background-research",
NodeDurableAgentRuntimeOptions<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never, ReconcilerError = never, ReconcilerRequirements = never>.producerId: string
producerId
: "worker-start-001",
NodeDurableAgentRuntimeOptions<ContextError = never, ContextRequirements = never, AuthorizationError = never, AuthorizationRequirements = never, ReconcilerError = never, ReconcilerRequirements = never>.workerConcurrency?: number | undefined

Default 1; bounded to 1..64.

workerConcurrency
: 4,
},
).
Pipeable.pipe<Layer.Layer<DurableAgentRuntime | MessageDeliveryStore | ThreadReader | NodeDurableAgentRuntimeConfig | (Crypto & DurableAgentRuntime) | (Crypto & MessageDeliveryStore) | (Crypto & ThreadReader) | (Crypto & NodeDurableAgentRuntimeConfig) | (SqlClient & DurableAgentRuntime) | (SqlClient & MessageDeliveryStore) | ... 6 more ... | NodeDurableHost.NodeDurableHost, DurableWorkerFailure | ... 2 more ... | NodePlatformConfigError, Handler<...> | ... 2 more ... | OpenAiClient>, Layer.Layer<...>, Layer.Layer<...>, Layer.Layer<...>, Layer.Layer<...>>(this: Layer.Layer<...>, ab: (_: Layer.Layer<...>) => Layer.Layer<...>, bc: (_: Layer.Layer<...>) => Layer.Layer<...>, cd: (_: Layer.Layer<...>) => Layer.Layer<...>, de: (_: Layer.Layer<...>) => Layer.Layer<...>): Layer.Layer<...> (+21 overloads)
pipe
(
import Layer
Layer
.
const provide: <never, never, HandlersFor<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>>(that: Layer.Layer<HandlersFor<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<...>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<...>) => Layer.Layer<...> (+3 overloads)

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.

Example (Providing layer dependencies)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const logs: Array<string> = []
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies to UserService layer
const userServiceWithDependencies = userServiceLayer.pipe(
Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now UserService layer has no dependencies
const program = Effect.gen(function*() {
const userService = yield* UserService
return yield* userService.getUser("123")
}).pipe(
Effect.provide(userServiceWithDependencies)
)
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
(
const ResearchBackground: Readonly<{
tools: BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>;
toolkit: Toolkit<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<...>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>;
layer: Layer.Layer<...>;
}>
ResearchBackground
.
layer: Layer.Layer<HandlersFor<BackgroundTools<"activity-researcher", Struct<{
readonly city: String;
readonly focus: String;
}>, Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>, Never, {
readonly start: true;
readonly followUp: true;
readonly reportToParent: true;
}>>, never, never>
layer
),
import Layer
Layer
.
const provide: <never, never, Handler<"search_activities">>(that: Layer.Layer<Handler<"search_activities">, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, E2, Exclude<RIn2, Handler<"search_activities">>> (+3 overloads)

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.

Example (Providing layer dependencies)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const logs: Array<string> = []
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies to UserService layer
const userServiceWithDependencies = userServiceLayer.pipe(
Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now UserService layer has no dependencies
const program = Effect.gen(function*() {
const userService = yield* UserService
return yield* userService.getUser("123")
}).pipe(
Effect.provide(userServiceWithDependencies)
)
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
(
const TravelToolsLive: Layer.Layer<Handler<"search_activities">, never, never>
TravelToolsLive
),
import Layer
Layer
.
const provide: <never, never, never>(that: Layer.Layer<never, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, E2, Exclude<RIn2, never>> (+3 overloads)

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.

Example (Providing layer dependencies)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const logs: Array<string> = []
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies to UserService layer
const userServiceWithDependencies = userServiceLayer.pipe(
Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now UserService layer has no dependencies
const program = Effect.gen(function*() {
const userService = yield* UserService
return yield* userService.getUser("123")
}).pipe(
Effect.provide(userServiceWithDependencies)
)
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
(
const WorkerAccessLive: Layer.Layer<never, never, never>
WorkerAccessLive
),
import Layer
Layer
.
const provide: <never, ConfigError, OpenAiClient>(that: Layer.Layer<OpenAiClient, ConfigError, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, ConfigError | E2, Exclude<RIn2, OpenAiClient>> (+3 overloads)

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.

Example (Providing layer dependencies)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
// Create dependency layers
const databaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
})
const logs: Array<string> = []
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(`[LOG] ${msg}`)))
})
// UserService depends on Database and Logger
const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
const database = yield* Database
const logger = yield* Logger
return {
getUser: Effect.fn("UserService.getUser")(function*(id: string) {
yield* logger.log(`Looking up user ${id}`)
const result = yield* database.query(
`SELECT * FROM users WHERE id = ${id}`
)
return { id, name: result }
})
}
}))
// Provide dependencies to UserService layer
const userServiceWithDependencies = userServiceLayer.pipe(
Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now UserService layer has no dependencies
const program = Effect.gen(function*() {
const userService = yield* UserService
return yield* userService.getUser("123")
}).pipe(
Effect.provide(userServiceWithDependencies)
)
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
(
const OpenAiLive: Layer.Layer<OpenAiClient, ConfigError, never>
OpenAiLive
),
);

Save as background-host.ts. Each entry registers an agent and its code versions with the host. The host discovers reporting from the coordinator’s background tools. The Layers supply tool handlers, provider credentials, and worker access.

The host recovers accepted work and pending reports after restarts. Keep report preparation free of external side effects: recovery may repeat it before its decision is recorded. Conclusive refusal before destination admission closes the retained source input and releases its capacity without a destination receipt or acknowledgement. Ambiguous delivery remains owed.

background-access.ts
import {
type ThreadId = string & Brand<"@effect-agent/core/ThreadId">
const ThreadId: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/ThreadId">

Identity shared by runs that participate in one thread history.

ThreadId
} from "@yielded/agent/identifiers";
import {
type Principal = string & Brand<"@effect-agent/thread/Principal">
const Principal: Schema.brand<Schema.NonEmptyString, "@effect-agent/thread/Principal">

Stable host-authenticated admission principal; the established durable brand is preserved.

Principal
} from "@yielded/agent/submission-ledger";
import {
class WorkerError

Closed host-boundary failures. Original causes stay private and survive diagnostic transport. Retained delivery states are successful MessageStatus values, including pending and refused. After a storage failure, keep the same idempotency key and parameters when reconciling.

WorkerError
} from "@yielded/agent/worker";
import {
const WorkerHostAuthorizer: Reference<{
readonly authorize: (request: WorkerHostAuthorizationRequest) => Effect.Effect<Principal, WorkerError>;
}>
WorkerHostAuthorizer
} from "@yielded/agent/worker-host";
import {
import Effect
Effect
,
import Layer
Layer
,
import Schema
Schema
} from "effect";
// This local example permits one user to manage workers from one conversation.
export const
const principal: string & Brand<"@effect-agent/thread/Principal">
principal
=
import Schema
Schema
.
const decodeSync: <Schema.brand<Schema.NonEmptyString, "@effect-agent/thread/Principal">>(schema: Schema.brand<Schema.NonEmptyString, "@effect-agent/thread/Principal">, options?: ParseOptions) => (input: string, options?: ParseOptions) => string & Brand<"@effect-agent/thread/Principal">

Decodes a typed input (the schema's Encoded type) against a schema synchronously, returning the decoded value or throwing a

SchemaError

for schema mismatches.

When to use

Use when you already have input typed as the schema's Encoded type and want schema mismatches to throw SchemaError synchronously.

Details

For unknown input use decodeUnknownSync. Only service-free schemas can be decoded synchronously. Options may be provided either when creating the decoder or when applying it; application options override creation options.

Gotchas

Non-schema failures may throw a runtime failure instead of SchemaError.

@see ― SchemaParser.decodeSync for the adapter that throws an Error whose cause is SchemaIssue.Issue

@category ― decoding

@since ― 4.0.0

decodeSync
(
const Principal: Schema.brand<Schema.NonEmptyString, "@effect-agent/thread/Principal">

Stable host-authenticated admission principal; the established durable brand is preserved.

Principal
)("travel-user");
export const
const threadId: string & Brand<"@effect-agent/core/ThreadId">
threadId
=
import Schema
Schema
.
const decodeSync: <Schema.brand<Schema.NonEmptyString, "@effect-agent/core/ThreadId">>(schema: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/ThreadId">, options?: ParseOptions) => (input: string, options?: ParseOptions) => string & Brand<"@effect-agent/core/ThreadId">

Decodes a typed input (the schema's Encoded type) against a schema synchronously, returning the decoded value or throwing a

SchemaError

for schema mismatches.

When to use

Use when you already have input typed as the schema's Encoded type and want schema mismatches to throw SchemaError synchronously.

Details

For unknown input use decodeUnknownSync. Only service-free schemas can be decoded synchronously. Options may be provided either when creating the decoder or when applying it; application options override creation options.

Gotchas

Non-schema failures may throw a runtime failure instead of SchemaError.

@see ― SchemaParser.decodeSync for the adapter that throws an Error whose cause is SchemaIssue.Issue

@category ― decoding

@since ― 4.0.0

decodeSync
(
const ThreadId: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/ThreadId">

Identity shared by runs that participate in one thread history.

ThreadId
)("travel-chat");
export const
const WorkerAccessLive: Layer.Layer<never, never, never>
WorkerAccessLive
=
import Layer
Layer
.
const succeed: <never, {
readonly authorize: (request: WorkerHostAuthorizationRequest) => Effect.Effect<Principal, WorkerError>;
}>(service: Key<never, {
readonly authorize: (request: WorkerHostAuthorizationRequest) => Effect.Effect<Principal, WorkerError>;
}>) => (resource: {
readonly authorize: (request: WorkerHostAuthorizationRequest) => Effect.Effect<Principal, WorkerError>;
}) => Layer.Layer<...> (+1 overload)

Constructs a layer that provides a single service from an already available value.

When to use

Use when you need a Layer that provides a service from an already constructed implementation without effectful acquisition.

Example (Creating a layer from a service implementation)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const DatabaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Query result: ${sql}`))
})
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, DatabaseLayer)) // => "Query result: SELECT 1"

@see ― sync for constructing layers from lazy values

@category ― constructors

@since ― 2.0.0

succeed
(
const WorkerHostAuthorizer: Reference<{
readonly authorize: (request: WorkerHostAuthorizationRequest) => Effect.Effect<Principal, WorkerError>;
}>
WorkerHostAuthorizer
)({
authorize: (request: WorkerHostAuthorizationRequest) => Effect.Effect<Principal, WorkerError>
authorize
: (
request: WorkerHostAuthorizationRequest
request
) =>
request: WorkerHostAuthorizationRequest
request
.
WorkerHostAuthorizationRequest.principal: string & Brand<"@effect-agent/thread/Principal">
principal
===
const principal: string & Brand<"@effect-agent/thread/Principal">
principal
&&
request: WorkerHostAuthorizationRequest
request
.
WorkerHostAuthorizationRequest.sourceThreadId: string & Brand<"@effect-agent/core/ThreadId">
sourceThreadId
===
const threadId: string & Brand<"@effect-agent/core/ThreadId">
threadId
?
import Effect
Effect
.
const succeed: <string & Brand<"@effect-agent/thread/Principal">>(value: string & Brand<"@effect-agent/thread/Principal">) => Effect.Effect<string & Brand<"@effect-agent/thread/Principal">, never, never>

Creates an Effect that always succeeds with a given value.

When to use

Use when an effect should complete successfully with a specific value without any errors or external dependencies.

Example (Creating a successful effect)

import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
Effect.runSync(success) // => 42

@see ― fail to create an effect that represents a failure.

@category ― constructors

@since ― 2.0.0

succeed
(
const principal: string & Brand<"@effect-agent/thread/Principal">
principal
)
:
class WorkerError

Closed host-boundary failures. Original causes stay private and survive diagnostic transport. Retained delivery states are successful MessageStatus values, including pending and refused. After a storage failure, keep the same idempotency key and parameters when reconciling.

WorkerError
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<WorkerError, { readonly operation: "context" | "start" | "followUp" | "inspect" | "observe" | "await" | "list" | "cancel" | "stop"; ... 4 more ...; readonly retryable?: true | undefined; }, readonly [...], { ...; }>, TaggedStruct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly operation: "context" | "start" | "followUp" | "inspect" | "observe" | "await" | "list" | "cancel" | "stop";
readonly reason: "denied" | "declaration-unavailable" | "worker-mismatch" | "receipt-mismatch" | "message-mismatch" | "idempotency-conflict" | "capacity" | "not-found" | "delivery-pending" | "storage" | "corrupt" | "unavailable";
readonly cause?: unknown;
readonly stack?: string | undefined;
readonly retryable?: true | undefined;
readonly _tag?: "WorkerError" | undefined;
}, options?: Schema.MakeOptions): WorkerError

Constructs a value from the make input representation synchronously.

When to use

Use when constructor input is trusted or when validation failure should abort with a thrown Error.

Details

Applies constructor defaults and type-side validation according to MakeOptions.

Gotchas

Throws an Error with the schema issue in its cause when validation fails. Schema validation failures use the generic message "Schema validation failed"; format the cause explicitly with SchemaIssue.makeFormatterDefault() when human-readable details are needed. Causes that contain defects, interruptions, or other non-schema reasons throw with the underlying Cause attached instead.

@see ― BottomWithoutNew.makeOption — construct synchronously and discard validation details

@see ― BottomWithoutNew.makeEffect — construct through Effect when validation failure should stay in the error channel

make
({
operation: "context" | "start" | "followUp" | "inspect" | "observe" | "await" | "list" | "cancel" | "stop"
operation
:
request: WorkerHostAuthorizationRequest
request
.
WorkerHostAuthorizationRequest.operation: "context" | "start" | "followUp" | "inspect" | "observe" | "await" | "list" | "cancel" | "stop"
operation
,
reason: "denied" | "declaration-unavailable" | "worker-mismatch" | "receipt-mismatch" | "message-mismatch" | "idempotency-conflict" | "capacity" | "not-found" | "delivery-pending" | "storage" | "corrupt" | "unavailable"
reason
: "denied" }),
});

Save as background-access.ts. Worker access denies by default; this local example permits one user and conversation. In an application, check authenticated identity and thread ownership.

background-main.ts
import {
import NodeRuntime
NodeRuntime
} from "@effect/platform-node";
import {
import NodeDurableHost
NodeDurableHost
} from "@yielded/agent-platform-node";
import {
import Effect
Effect
} from "effect";
import {
const HostLive: Layer<DurableAgentRuntime | MessageDeliveryStore | ThreadReader | NodeDurableAgentRuntimeConfig | (Crypto & DurableAgentRuntime) | (Crypto & MessageDeliveryStore) | (Crypto & ThreadReader) | (Crypto & NodeDurableAgentRuntimeConfig) | (SqlClient & DurableAgentRuntime) | (SqlClient & MessageDeliveryStore) | ... 6 more ... | NodeDurableHost.NodeDurableHost, DurableWorkerFailure | ... 3 more ... | ConfigError, never>
HostLive
} from "./background-host.ts";
import NodeRuntime
NodeRuntime
.
const runMain: <BindingUnavailable | DurableWorkerFailure | MessageDeliveryError | SqliteStorageInitializationError | NodePlatformConfigError | ConfigError, void>(effect: Effect.Effect<void, BindingUnavailable | DurableWorkerFailure | MessageDeliveryError | SqliteStorageInitializationError | NodePlatformConfigError | ConfigError, never>, options?: {
readonly disableErrorReporting?: boolean | undefined;
readonly teardown?: Teardown | undefined;
}) => void (+1 overload)

Helps you run a main effect with built-in error handling, logging, and signal management.

When to use

Use to run a Node.js application's main Effect with structured error handling, log management, interrupt support, or advanced teardown capabilities.

Details

This function launches an Effect as the main entry point, setting exit codes based on success or failure, handling interrupts (e.g., Ctrl+C), and optionally logging errors. By default, it logs errors and uses a "pretty" format, but both behaviors can be turned off. You can also provide custom teardown logic to finalize resources or produce different exit codes.

The optional configuration object can include:

  • disableErrorReporting: Turn off automatic error logging.
  • teardown: Provide custom finalization logic.

@category ― running

@since ― 4.0.0

runMain
(
import NodeDurableHost
NodeDurableHost
.
const run: Effect.Effect<void, BindingUnavailable | DurableWorkerFailure, NodeDurableHost.NodeDurableHost>

Supervise the host's existing workers without starting another pool. Use with Effect.provide(HostLive) and NodeRuntime.runMain; race it with a server Effect when the same process also serves requests. Unlike Layer.launch, this observes worker failures.

run
.
Pipeable.pipe<Effect.Effect<void, BindingUnavailable | DurableWorkerFailure, NodeDurableHost.NodeDurableHost>, Effect.Effect<void, BindingUnavailable | DurableWorkerFailure | MessageDeliveryError | SqliteStorageInitializationError | NodePlatformConfigError | ConfigError, never>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<void, BindingUnavailable | DurableWorkerFailure, NodeDurableHost.NodeDurableHost>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const provide: <DurableAgentRuntime | MessageDeliveryStore | ThreadReader | NodeDurableAgentRuntimeConfig | (Crypto & DurableAgentRuntime) | (Crypto & MessageDeliveryStore) | (Crypto & ThreadReader) | (Crypto & NodeDurableAgentRuntimeConfig) | (SqlClient & DurableAgentRuntime) | (SqlClient & MessageDeliveryStore) | ... 6 more ... | NodeDurableHost.NodeDurableHost, DurableWorkerFailure | ... 3 more ... | ConfigError, never>(layer: Layer<...>, options?: {
readonly local?: boolean | undefined;
} | undefined) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+5 overloads)

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.

Example (Providing dependencies with a layer)

import { Context, Effect, Layer } from "effect"
interface Database {
readonly query: (sql: string) => Effect.Effect<string>
}
const Database = Context.Service<Database>("Database")
const DatabaseLayer = Layer.succeed(Database)({
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result for: ${sql}`))
})
const program = Effect.gen(function*() {
const db = yield* Database
return yield* db.query("SELECT * FROM users")
})
const provided = Effect.provide(program, DatabaseLayer)
await Effect.runPromise(provided) // => "Result for: SELECT * FROM users"

@category ― providing services

@since ― 2.0.0

provide
(
const HostLive: Layer<DurableAgentRuntime | MessageDeliveryStore | ThreadReader | NodeDurableAgentRuntimeConfig | (Crypto & DurableAgentRuntime) | (Crypto & MessageDeliveryStore) | (Crypto & ThreadReader) | (Crypto & NodeDurableAgentRuntimeConfig) | (SqlClient & DurableAgentRuntime) | (SqlClient & MessageDeliveryStore) | ... 6 more ... | NodeDurableHost.NodeDurableHost, DurableWorkerFailure | ... 3 more ... | ConfigError, never>
HostLive
)));

Save as background-main.ts and run with node --experimental-transform-types background-main.ts. Use the Node.js setup to submit BackgroundCoordinator with the exported principal, threadId, and this input:

{ "text": "Find food and walking activities in Lisbon." }

Keep the host running so research and report delivery can progress. The Cloudflare runtime supports the same contracts.

A follow-up returns its retained delivery state and joins an active worker run at a safe input boundary or starts a later run. Keep the returned message reference: inspect that delivery or wait for a report instead of sending the command again. Opt in to inspect, list, or cancel tools when needed. Inspection accepts a message reference for delivery state or a receipt for a saved result. Cancellation targets one input’s receipt; it does not close the worker. Application code can permanently seal the worker with Subagent.stop and a stable command key. See the control contract for stop and input application facts. Several inputs joining one run produce one logical report. An input cancelled before it starts a run produces no completion message.

Workers share a bounded allocation from their source by default. Host lifetime and concurrency limits still apply. See independent budgets for separately funded work.

For application-driven starts, see the programmatic API. For delivery failures and recovery, see report guarantees.