Skip to content

Subagents

In-memory attached subagents

Provide model services to the parent and child, then run the parent:

delegation-live.ts
import {
import OpenAiClient
OpenAiClient
,
import OpenAiLanguageModel
OpenAiLanguageModel
} from "@effect/ai-openai";
import {
import AgentRuntime
AgentRuntime
,
import InMemory
InMemory
,
import Subagent
Subagent
} from "@yielded/agent";
import {
import Config
Config
,
import Effect
Effect
,
import Layer
Layer
} from "effect";
import {
import FetchHttpClient
FetchHttpClient
} from "effect/http";
import {
const Coordinator: Definition<Struct<{
readonly city: String;
}>, Struct<{
readonly itinerary: $Array<String>;
}>, string, Toolkit<{
readonly delegate_research_activities: Tool<"delegate_research_activities", {
readonly parameters: Struct<{
readonly city: String;
readonly focus: String;
}>;
readonly success: Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>;
readonly failure: Subagent.SubagentToolFailure<Never>;
readonly failureMode: "error";
}, AgentRuntime.AgentSpawner | ... 1 more ... | AgentRuntime.SubagentDurability>;
}>, undefined, undefined, undefined> & {
...;
}
Coordinator
} from "./coordinator.ts";
import {
const Research: Subagent.SubagentDelegation<"delegate_research_activities", 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>;
}, ... 5 more ..., "error"> & {
...;
}
Research
} from "./delegation.ts";
import {
const TravelToolsLive: Layer.Layer<Handler<"search_activities">, never, never>
TravelToolsLive
} from "./tools.ts";
const
const ModelLive: Model<"openai", LanguageModel, OpenAiClient.OpenAiClient>
ModelLive
=
import OpenAiLanguageModel
OpenAiLanguageModel
.
const model: (model: (string & {}) | OpenAiLanguageModel.Model, config?: Omit<{
readonly metadata?: {
readonly [x: string]: string;
} | undefined;
readonly top_logprobs?: number | undefined;
readonly temperature?: number | undefined;
readonly top_p?: number | undefined;
readonly user?: string | undefined;
readonly prompt_cache_key?: string | undefined;
readonly prompt_cache_options?: {
readonly mode?: "explicit" | "implicit" | undefined;
readonly ttl?: "30m" | undefined;
} | undefined;
... 17 more ...;
readonly useItemReferences?: boolean | undefined;
}, "model">) => Model<"openai", LanguageModel, OpenAiClient.OpenAiClient>

Creates an OpenAI model descriptor that can be provided with Effect.provide.

When to use

Use when you want an OpenAI language model value that carries provider and model metadata and can be supplied directly to an Effect program.

@see ― layer for creating a LanguageModel.LanguageModel layer directly

@see ― make for constructing the language model service effectfully

@stability ― unstable

@category ― constructors

@since ― 4.0.0

model
("gpt-6-luna");
const
const ProviderLive: Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, never>
ProviderLive
=
import OpenAiClient
OpenAiClient
.
const layerConfig: (options?: {
readonly apiKey?: Config.Config<Redacted<string> | undefined> | undefined;
readonly apiUrl?: Config.Config<string> | undefined;
readonly organizationId?: Config.Config<Redacted<string> | undefined> | undefined;
readonly projectId?: Config.Config<Redacted<string> | undefined> | undefined;
readonly transformClient?: ((client: HttpClient) => HttpClient) | undefined;
}) => Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, HttpClient>

Creates a layer for the OpenAI client from provided Config values.

When to use

Use when you need client settings for OpenAI-compatible APIs to be read from Effect Config values while providing OpenAiClient as a Layer.

Details

Only config values supplied in options are loaded. Omitted fields are passed to make as undefined, and transformClient is forwarded as a plain option.

@see ― make for constructing the client service effectfully

@see ― layer for providing the client from already-resolved options

@stability ― unstable

@category ― layers

@since ― 4.0.0

layerConfig
({
apiKey?: Config.Config<Redacted<string> | undefined> | undefined

The config value to load for the API key.

apiKey
:
import Config
Config
.
function Redacted(name?: string): Config.Config<import("effect/Redacted").Redacted<string>>

Creates a config for a redacted string value. The parsed result is wrapped in a Redacted container that hides the value from logs and toString.

When to use

Use to read secret string settings that should not be exposed in logs or string output.

Details

Shortcut for Config.schema(Schema.Redacted(Schema.String), name).

Example (Reading a secret)

import { Config, ConfigProvider, Effect } from "effect"
const program = Config.Redacted("API_KEY").pipe(Effect.map(String))
const provider = ConfigProvider.fromEnv({
env: {
API_KEY: "sk-1234567890abcdef"
}
})
Effect.runSync(
program.pipe(Effect.provideService(ConfigProvider.ConfigProvider, provider))
) // => "<redacted>"

@see ― String for non-secret string settings

@category ― constructors

@since ― 2.0.0

Redacted
("OPENAI_API_KEY") }).
Pipeable.pipe<Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, HttpClient>, Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, never>>(this: Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, HttpClient>, ab: (_: Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, HttpClient>) => Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, never>): Layer.Layer<...> (+21 overloads)
pipe
(
import Layer
Layer
.
const provide: <never, never, HttpClient>(that: Layer.Layer<HttpClient, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, E2, Exclude<RIn2, HttpClient>> (+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
(
import FetchHttpClient
FetchHttpClient
.
const layer: Layer.Layer<HttpClient, never, never>

Layer that provides an HttpClient implementation backed by the configured Fetch function.

When to use

Use when an Effect program should execute HttpClient requests through the platform fetch implementation, especially in browser, edge, or Node.js runtimes with globalThis.fetch.

Details

The layer uses the current Fetch reference and optional RequestInit service for each request. Request-specific method, headers, body, and abort signal are supplied by the client and override matching RequestInit fields.

Gotchas

Fetch behavior comes from the runtime's implementation, so CORS, cookies, redirects, abort handling, and streaming support can vary by platform. Stream request bodies are sent as Web streams with duplex: "half", and any content-length header is removed before calling fetch.

@see ― Fetch for supplying the fetch implementation used by this layer

@see ― RequestInit for default RequestInit options applied before request-specific fields

@stability ― unstable

@category ― layers

@since ― 4.0.0

layer
),
);
const
const ResearchLive: Layer.Layer<Handler<"delegate_research_activities">, never, SubagentReservations | ModelServices>
ResearchLive
=
import Subagent
Subagent
.
function layer<"delegate_research_activities", 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>;
}, Struct<...>, Struct<...>, Never, never, never, never, "error", never, never, undefined, undefined, undefined>(delegation: Subagent.SubagentDelegation<...> & {
...;
}, modelOrBinding?: undefined, options?: Subagent.SubagentRuntimeOptions<...> | undefined): Layer.Layer<...> (+1 overload)

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.

layer
(
const Research: Subagent.SubagentDelegation<"delegate_research_activities", 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>;
}, ... 5 more ..., "error"> & {
...;
}
Research
).
Pipeable.pipe<Layer.Layer<Handler<"delegate_research_activities">, never, Subagent.SubagentLayerRequirements<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>;
}, ... 10 more ..., undefined>>, Layer.Layer<...>>(this: Layer.Layer<...>, ab: (_: Layer.Layer<...>) => Layer.Layer<...>): Layer.Layer<...> (+21 overloads)
pipe
(
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
));
const
const AppLive: Layer.Layer<Handler<"delegate_research_activities"> | LanguageModel | ThreadHistory | ProviderName | ModelName | SubagentReservations | Store, Config.ConfigError, never>
AppLive
=
const ResearchLive: Layer.Layer<Handler<"delegate_research_activities">, never, SubagentReservations | ModelServices>
ResearchLive
.
Pipeable.pipe<Layer.Layer<Handler<"delegate_research_activities">, never, SubagentReservations | ModelServices>, Layer.Layer<Handler<"delegate_research_activities"> | LanguageModel | ProviderName | ModelName, never, OpenAiClient.OpenAiClient | SubagentReservations>, Layer.Layer<Handler<"delegate_research_activities"> | LanguageModel | ThreadHistory | ProviderName | ModelName | SubagentReservations | Store, never, OpenAiClient.OpenAiClient>, Layer.Layer<...>>(this: Layer.Layer<...>, ab: (_: Layer.Layer<...>) => Layer.Layer<...>, bc: (_: Layer.Layer<...>) => Layer.Layer<...>, cd: (_: Layer.Layer<...>) => Layer.Layer<...>): Layer.Layer<...> (+21 overloads)
pipe
(
import Layer
Layer
.
const provideMerge: <OpenAiClient.OpenAiClient, never, LanguageModel | ProviderName | ModelName>(that: Layer.Layer<LanguageModel | ProviderName | ModelName, never, OpenAiClient.OpenAiClient>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<LanguageModel | ProviderName | ModelName | ROut2, E2, OpenAiClient.OpenAiClient | Exclude<...>> (+3 overloads)

Feeds the output services of the dependency layer into the requirements of this layer, returning a layer that provides both sets of services.

When to use

Use when you need to compose Layers while keeping both the constructed service and the dependency used to build it available.

Details

Prefer

provide

when the dependency should stay private.

Example (Providing dependencies while retaining services)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
// 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 and merge all services together
const allServicesLayer = userServiceLayer.pipe(
Layer.provideMerge(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now the resulting layer provides UserService, Database, AND Logger
const program = Effect.gen(function*() {
const userService = yield* UserService
const logger = yield* Logger // Still available!
const database = yield* Database // Still available!
const user = yield* userService.getUser("123")
yield* logger.log(`Found user: ${user.name}`)
return user
}).pipe(
Effect.provide(allServicesLayer)
)
Effect.runSync(program) // => { id: "123", name: "DB: SELECT * FROM users WHERE id = 123" }
logs // => ["[LOG] Looking up user 123", "[LOG] Found user: DB: SELECT * FROM users WHERE id = 123"]

@see ― provide for keeping dependency services private

@category ― providing services

@since ― 2.0.0

provideMerge
(
const ModelLive: Model<"openai", LanguageModel, OpenAiClient.OpenAiClient>
ModelLive
),
import Layer
Layer
.
const provideMerge: <never, never, ThreadHistory | SubagentReservations | Store>(that: Layer.Layer<ThreadHistory | SubagentReservations | Store, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ThreadHistory | SubagentReservations | Store | ROut2, E2, Exclude<RIn2, ThreadHistory | SubagentReservations | Store>> (+3 overloads)

Feeds the output services of the dependency layer into the requirements of this layer, returning a layer that provides both sets of services.

When to use

Use when you need to compose Layers while keeping both the constructed service and the dependency used to build it available.

Details

Prefer

provide

when the dependency should stay private.

Example (Providing dependencies while retaining services)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
class Logger extends Context.Service<Logger, {
readonly log: (msg: string) => Effect.Effect<void>
}>()("Logger") {}
class UserService extends Context.Service<UserService, {
readonly getUser: (id: string) => Effect.Effect<{
id: string
name: string
}>
}>()("UserService") {}
// 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 and merge all services together
const allServicesLayer = userServiceLayer.pipe(
Layer.provideMerge(Layer.mergeAll(databaseLayer, loggerLayer))
)
// Now the resulting layer provides UserService, Database, AND Logger
const program = Effect.gen(function*() {
const userService = yield* UserService
const logger = yield* Logger // Still available!
const database = yield* Database // Still available!
const user = yield* userService.getUser("123")
yield* logger.log(`Found user: ${user.name}`)
return user
}).pipe(
Effect.provide(allServicesLayer)
)
Effect.runSync(program) // => { id: "123", name: "DB: SELECT * FROM users WHERE id = 123" }
logs // => ["[LOG] Looking up user 123", "[LOG] Found user: DB: SELECT * FROM users WHERE id = 123"]

@see ― provide for keeping dependency services private

@category ― providing services

@since ― 2.0.0

provideMerge
(
import InMemory
InMemory
.
const layer: Layer.Layer<ThreadHistory | SubagentReservations | Store, never, never>

Run agents and attached subagents with in-memory conversation history. Provide once around the parent program and all child handler Layers so siblings share history and one reservation ledger. Reuse a Thread ID to continue a conversation. Conversations can span any number of Runs within the store's capacity limits. Use scoped for disposable request workflows within this shared store. Each independent Layer build owns fresh state; state is released when its Scope closes. Process loss loses history and active execution; this Layer provides no crash recovery.

Models, tool handlers, and provider clients remain application-supplied. Default IDs need no Layer; enclosing ID and context-preparation overrides are preserved. For storage-backed history, provide PersistentHistory.layer and a shared SubagentReservationsMemoryLive instead. Durable hosts own their own assembly.

layer
),
import Layer
Layer
.
const provide: <never, Config.ConfigError, OpenAiClient.OpenAiClient>(that: Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, Config.ConfigError | E2, Exclude<RIn2, OpenAiClient.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 ProviderLive: Layer.Layer<OpenAiClient.OpenAiClient, Config.ConfigError, never>
ProviderLive
),
);
export const
const program: Effect.Effect<{
readonly output: {
readonly itinerary: readonly string[];
};
readonly turns: number;
readonly threadId: string & Brand<"@effect-agent/core/ThreadId">;
readonly runId: string & Brand<"@effect-agent/core/RunId">;
readonly finishReason: "completed" | "model-stop" | "budget-exhausted";
readonly runDisposition?: Json | undefined;
readonly exhausted?: "turns" | "tool-calls" | "tokens" | undefined;
readonly usage?: RunTotals | undefined;
readonly delegatedUsage?: RunTotals | undefined;
}, Config.ConfigError | AgentRuntime.AgentRuntimeFailure<...>, never>
program
=
import AgentRuntime
AgentRuntime
.
run<Definition<Struct<{
readonly city: String;
}>, Struct<{
readonly itinerary: $Array<String>;
}>, string, Toolkit<{
readonly delegate_research_activities: Tool<"delegate_research_activities", {
readonly parameters: Struct<{
readonly city: String;
readonly focus: String;
}>;
readonly success: Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>;
readonly failure: Subagent.SubagentToolFailure<Never>;
readonly failureMode: "error";
}, AgentRuntime.AgentSpawner | ... 1 more ... | AgentRuntime.SubagentDurability>;
}>, undefined, undefined, undefined> & {
...;
}, never, never>(agent: Definition<...> & {
...;
}, input: NoInfer<{
...;
}>, options?: RunOptions<...> | undefined): Effect.Effect<...>
export run

Accept schema-encoded input, retaining runtime validation. Use runUnknown for external data.

run
(
const Coordinator: Definition<Struct<{
readonly city: String;
}>, Struct<{
readonly itinerary: $Array<String>;
}>, string, Toolkit<{
readonly delegate_research_activities: Tool<"delegate_research_activities", {
readonly parameters: Struct<{
readonly city: String;
readonly focus: String;
}>;
readonly success: Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>;
readonly failure: Subagent.SubagentToolFailure<Never>;
readonly failureMode: "error";
}, AgentRuntime.AgentSpawner | ... 1 more ... | AgentRuntime.SubagentDurability>;
}>, undefined, undefined, undefined> & {
...;
}
Coordinator
, {
city: string
city
: "Lisbon" }).
Pipeable.pipe<Effect.Effect<{
readonly output: {
readonly itinerary: readonly string[];
};
readonly turns: number;
readonly threadId: string & Brand<"@effect-agent/core/ThreadId">;
readonly runId: string & Brand<"@effect-agent/core/RunId">;
readonly finishReason: "completed" | "model-stop" | "budget-exhausted";
readonly runDisposition?: Json | undefined;
readonly exhausted?: "turns" | "tool-calls" | "tokens" | undefined;
readonly usage?: RunTotals | undefined;
readonly delegatedUsage?: RunTotals | undefined;
}, AgentRuntime.AgentRuntimeFailure<Definition<Struct<{
...;
}>, ... 5 more ..., undefined> & {
...;
}, never, never>, AgentRuntime.AgentRuntimeRequirements<...>>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const provide: <Handler<"delegate_research_activities"> | LanguageModel | ThreadHistory | ProviderName | ModelName | SubagentReservations | Store, Config.ConfigError, never>(layer: Layer.Layer<Handler<"delegate_research_activities"> | LanguageModel | ThreadHistory | ProviderName | ModelName | SubagentReservations | Store, Config.ConfigError, never>, 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 AppLive: Layer.Layer<Handler<"delegate_research_activities"> | LanguageModel | ThreadHistory | ProviderName | ModelName | SubagentReservations | Store, Config.ConfigError, never>
AppLive
),
);

Coordinator calls the Research tool, waits for its findings, and builds an itinerary. Subagent.layer(Research) builds the child’s tool handlers and requires model services. Layer.provideMerge(ModelLive) supplies that model to the handlers and exposes it to the parent. To give a child its own model, provide it directly to that child’s handler Layer with Layer.provide(ChildModel).

InMemory.layer keeps parent and child conversations in memory and shares one reservation ledger across the parent’s subagents. Provide it once around all child handler Layers, as above. Reuse the parent’s Thread ID for follow-up Runs within that application Scope. Each child has its own Thread. IDs are generated automatically; context preparation is optional. Process loss loses this history and active execution.

The files below define Research, Coordinator, and the sample activity tools. Save them beside delegation-live.ts.

researcher.ts
import {
import Agent
Agent
} from "@yielded/agent";
import {
import Schema
Schema
} from "effect";
import {
const TravelTools: 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>;
}>
TravelTools
} from "./tools.ts";
export const
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
=
import Agent
Agent
.
function make<"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, 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>(id: "activity-researcher", options: Agent.DefinitionOptions<...> & {
...;
}): Agent.Definition<...> & {
...;
} (+3 overloads)

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

make
("activity-researcher", {
DefinitionOptions<Struct<{ readonly city: String; readonly focus: String; }>, Struct<{ readonly activities: $Array<String>; readonly researchNotes: String; }>, ... 4 more ..., undefined>.input: Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>
input
:
import Schema
Schema
.
function Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>(fields: {
readonly city: Schema.String;
readonly focus: Schema.String;
}): Schema.Struct<{
readonly city: Schema.String;
readonly focus: 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
,
focus: Schema.String
focus
:
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 focus: String; }>, Struct<{ readonly activities: $Array<String>; readonly researchNotes: String; }>, ... 4 more ..., undefined>.output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>
output
:
import Schema
Schema
.
function Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>(fields: {
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}): Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: 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
({
activities: Schema.$Array<Schema.String>
activities
:
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
),
researchNotes: Schema.String
researchNotes
:
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 focus: String; }>, Struct<{ readonly activities: $Array<String>; readonly researchNotes: String; }>, ... 4 more ..., undefined>.instructions: ({ city, focus }: {
readonly city: string;
readonly focus: string;
}) => string
instructions
: ({
city: string
city
,
focus: string
focus
}) =>
`Use search_activities to find activities in ${
city: string
city
}. Focus on ${
focus: string
focus
}. ` +
"Return matching activities and notes explaining your selection.",
DefinitionOptions<Struct<{ readonly city: String; readonly focus: String; }>, Struct<{ readonly activities: $Array<String>; readonly researchNotes: String; }>, ... 4 more ..., undefined>.toolkit: 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>;
}>
toolkit
:
const TravelTools: 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>;
}>
TravelTools
,
DefinitionOptions<Struct<{ readonly city: String; readonly focus: String; }>, Struct<{ readonly activities: $Array<String>; readonly researchNotes: String; }>, ... 4 more ..., 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
: {
maxToolCalls?: number | undefined
maxToolCalls
: 8,
},
});
tools.ts
import {
import Effect
Effect
,
import Schema
Schema
} from "effect";
import {
import Tool
Tool
,
import Toolkit
Toolkit
} from "effect/ai";
const
const SearchActivities: Tool.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>
SearchActivities
=
import Tool
Tool
.
const make: <"search_activities", Schema.Struct<{
readonly city: Schema.String;
}>, Schema.$Array<Schema.String>, Schema.Never, undefined, []>(name: "search_activities", options?: {
readonly description?: string | undefined;
readonly parameters?: Schema.Struct<{
readonly city: Schema.String;
}> | undefined;
readonly success?: Schema.$Array<Schema.String> | undefined;
readonly failure?: Schema.Never | undefined;
readonly failureMode?: undefined;
readonly dependencies?: [] | undefined;
readonly needsApproval?: Tool.NeedsApproval<...> | undefined;
} | undefined) => Tool.Tool<...>

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
const GetCurrentTime = Tool.make("GetCurrentTime", {
description: "Returns the current timestamp",
success: Schema.Number
})
GetCurrentTime.name // => "GetCurrentTime"

@stability ― unstable

@category ― constructors

@since ― 4.0.0

make
("search_activities", {
description?: string | undefined

An optional description explaining what the tool does.

description
: "Find activities in a city.",
parameters?: Schema.Struct<{
readonly city: Schema.String;
}> | undefined

Schema defining the parameters this tool accepts.

parameters
:
import Schema
Schema
.
function Struct<{
readonly city: Schema.String;
}>(fields: {
readonly city: Schema.String;
}): Schema.Struct<{
readonly city: 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
}),
success?: Schema.$Array<Schema.String> | undefined

Schema for successful tool execution results.

success
:
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 TravelTools: Toolkit.Toolkit<{
readonly search_activities: Tool.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>;
}>
TravelTools
=
import Toolkit
Toolkit
.
const make: <[Tool.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>]>(tools_0: Tool.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>) => Toolkit.Toolkit<...>

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.

Example (Creating a toolkit)

import { Effect, Schema } from "effect"
import { Tool, Toolkit } from "effect/ai"
const GetCurrentTime = Tool.make("GetCurrentTime", {
description: "Get the current timestamp",
success: Schema.Number
})
const GetWeather = Tool.make("get_weather", {
description: "Get weather information",
parameters: Schema.Struct({ location: Schema.String }),
success: Schema.Struct({
temperature: Schema.Number,
condition: Schema.String
})
})
const toolkit = Toolkit.make(GetCurrentTime, GetWeather)
const ready = toolkit.pipe(Effect.provide(toolkit.toLayer({
GetCurrentTime: () => Effect.succeed(0),
get_weather: () => Effect.succeed({ temperature: 20, condition: "clear" })
})))
Object.keys((await Effect.runPromise(ready)).tools) // => ["GetCurrentTime", "get_weather"]

@stability ― unstable

@category ― constructors

@since ― 4.0.0

make
(
const SearchActivities: Tool.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>
SearchActivities
);
// Sample data. A real handler can query your database or a travel API.
const
const activities: {
city: string;
name: string;
}[]
activities
= [
{
city: string
city
: "Lisbon",
name: string
name
: "Riverside walk" },
{
city: string
city
: "Lisbon",
name: string
name
: "Food market" },
{
city: string
city
: "Lisbon",
name: string
name
: "City museum" },
];
export const
const TravelToolsLive: Layer<Tool.Handler<"search_activities">, never, never>
TravelToolsLive
=
const TravelTools: Toolkit.Toolkit<{
readonly search_activities: Tool.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>;
}>
TravelTools
.
Toolkit<{ readonly search_activities: Tool<"search_activities", { readonly parameters: Struct<{ readonly city: String; }>; readonly success: $Array<String>; readonly failure: Never; readonly failureMode: "error"; }, never>; }>.toLayer<{
search_activities: ({ city }: {
readonly city: string;
}) => Effect.Effect<string[], never, never>;
}, never, never>(build: {
search_activities: ({ city }: {
readonly city: string;
}) => Effect.Effect<string[], never, never>;
} | Effect.Effect<{
search_activities: ({ city }: {
readonly city: string;
}) => Effect.Effect<string[], never, never>;
}, never, never>): Layer<Tool.Handler<"search_activities">, never, never>

Converts a toolkit into a Layer containing handlers for each tool in the toolkit.

toLayer
({
search_activities: ({ city }: {
readonly city: string;
}) => Effect.Effect<string[], never, never>
search_activities
: ({
city: string
city
}) =>
import Effect
Effect
.
const succeed: <string[]>(value: string[]) => Effect.Effect<string[], 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 activities: {
city: string;
name: string;
}[]
activities
.
Array<{ city: string; name: string; }>.filter(predicate: (value: {
city: string;
name: string;
}, index: number, array: {
city: string;
name: string;
}[]) => unknown, thisArg?: any): {
city: string;
name: string;
}[] (+1 overload)

Returns the elements of an array that meet the condition specified in a callback function.

@param ― predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.

@param ― thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.

filter
((
a: {
city: string;
name: string;
}
a
) =>
a: {
city: string;
name: string;
}
a
.
city: string
city
===
city: string
city
).
Array<{ city: string; name: string; }>.map<string>(callbackfn: (value: {
city: string;
name: string;
}, index: number, array: {
city: string;
name: string;
}[]) => string, thisArg?: any): string[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

@param ― thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

map
((
a: {
city: string;
name: string;
}
a
) =>
a: {
city: string;
name: string;
}
a
.
name: string
name
)),
});

Researcher receives a city and focus. Its search_activities tool uses sample data, so only the model needs an API key. The child’s tool calls stay in its own conversation.

delegation.ts
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";
export const
const Research: Subagent.SubagentDelegation<"delegate_research_activities", 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>;
}, ... 5 more ..., "error"> & {
...;
}
Research
=
import Subagent
Subagent
.
make<"delegate_research_activities", 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>;
}, Struct<...>, Struct<...>, Never, never, never, Definition<...> & {
...;
}>(name: "delegate_research_activities", options: Omit<...> & ... 1 more ... & {
...;
}): Subagent.SubagentDelegation<...> & {
...;
} (+1 overload)
export make

Expose one child Agent as an attached Effect AI Tool. The nonempty application name is preserved as both Tool name and delegation identity; no prefix is required. Parameters and result projections default to the child's input and result envelope. Throws when the name is empty. Nested Tool visibility follows the effective inherited grant.

make
("delegate_research_activities", {
target: 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>;
}>, RunDispositionDeclaration<...> | undefined, unknown, Top | undefined> & Definition<...> & {
...;
}

The model-agnostic child Agent Definition this delegation targets (SUB-002).

target
:
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
});

Subagent.make uses the child’s input Schema as its tool parameters and returns { output, budgetExhausted }. The parent policy supplies a shared delegation budget, and the child’s own policy can tighten its limits. Expected child failures become bounded SubagentExecutionFailure values without a custom error Schema or mapper.

coordinator.ts
import {
import Agent
Agent
} from "@yielded/agent";
import {
import Schema
Schema
} from "effect";
import {
import Toolkit
Toolkit
} from "effect/ai";
import {
const Research: SubagentDelegation<"delegate_research_activities", 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>;
}, ... 5 more ..., "error"> & {
...;
}
Research
} from "./delegation.ts";
export const
const Coordinator: Agent.Definition<Schema.Struct<{
readonly city: Schema.String;
}>, Schema.Struct<{
readonly itinerary: Schema.$Array<Schema.String>;
}>, string, Toolkit.Toolkit<{
readonly delegate_research_activities: Tool<"delegate_research_activities", {
readonly parameters: Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>;
readonly success: Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>;
readonly failure: SubagentToolFailure<...>;
readonly failureMode: "error";
}, AgentSpawner | ... 1 more ... | SubagentDurability>;
}>, undefined, undefined, undefined> & {
...;
}
Coordinator
=
import Agent
Agent
.
function make<"trip-coordinator", Schema.Struct<{
readonly city: Schema.String;
}>, Schema.Struct<{
readonly itinerary: Schema.$Array<Schema.String>;
}>, string, Toolkit.Toolkit<{
readonly delegate_research_activities: Tool<"delegate_research_activities", {
readonly parameters: Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>;
readonly success: Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>;
readonly failure: SubagentToolFailure<...>;
readonly failureMode: "error";
}, AgentSpawner | ... 1 more ... | SubagentDurability>;
}>, undefined>(id: "trip-coordinator", options: Agent.DefinitionOptions<...> & {
...;
}): Agent.Definition<...> & {
...;
} (+3 overloads)

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

make
("trip-coordinator", {
DefinitionOptions<Struct<{ readonly city: String; }>, Struct<{ readonly itinerary: $Array<String>; }>, string, Toolkit<{ readonly delegate_research_activities: Tool<...>; }>, undefined, undefined, undefined>.input: Schema.Struct<{
readonly city: Schema.String;
}>
input
:
import Schema
Schema
.
function Struct<{
readonly city: Schema.String;
}>(fields: {
readonly city: Schema.String;
}): Schema.Struct<{
readonly city: 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
}),
DefinitionOptions<Struct<{ readonly city: String; }>, Struct<{ readonly itinerary: $Array<String>; }>, string, Toolkit<{ readonly delegate_research_activities: Tool<...>; }>, undefined, undefined, undefined>.output: Schema.Struct<{
readonly itinerary: Schema.$Array<Schema.String>;
}>
output
:
import Schema
Schema
.
function Struct<{
readonly itinerary: Schema.$Array<Schema.String>;
}>(fields: {
readonly itinerary: Schema.$Array<Schema.String>;
}): Schema.Struct<{
readonly itinerary: Schema.$Array<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
({
itinerary: Schema.$Array<Schema.String>
itinerary
:
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
) }),
DefinitionOptions<Struct<{ readonly city: String; }>, Struct<{ readonly itinerary: $Array<String>; }>, string, Toolkit<{ readonly delegate_research_activities: Tool<...>; }>, undefined, undefined, undefined>.instructions: string
instructions
:
"Call delegate_research_activities for the requested city with a focus on food and walking. " +
"Build an itinerary from the returned output.activities. If budgetExhausted is true, use only confirmed findings.",
DefinitionOptions<Struct<{ readonly city: String; }>, Struct<{ readonly itinerary: $Array<String>; }>, string, Toolkit<{ readonly delegate_research_activities: Tool<...>; }>, undefined, undefined, undefined>.toolkit: Toolkit.Toolkit<{
readonly delegate_research_activities: Tool<"delegate_research_activities", {
readonly parameters: Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>;
readonly success: Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>;
readonly failure: SubagentToolFailure<...>;
readonly failureMode: "error";
}, AgentSpawner | ... 1 more ... | SubagentDurability>;
}>
toolkit
:
import Toolkit
Toolkit
.
const make: <[Tool<"delegate_research_activities", {
readonly parameters: Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>;
readonly success: Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>;
readonly failure: SubagentToolFailure<Schema.Never>;
readonly failureMode: "error";
}, AgentSpawner | ... 1 more ... | SubagentDurability>]>(tools_0: Tool<...>) => Toolkit.Toolkit<...>

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.

Example (Creating a toolkit)

import { Effect, Schema } from "effect"
import { Tool, Toolkit } from "effect/ai"
const GetCurrentTime = Tool.make("GetCurrentTime", {
description: "Get the current timestamp",
success: Schema.Number
})
const GetWeather = Tool.make("get_weather", {
description: "Get weather information",
parameters: Schema.Struct({ location: Schema.String }),
success: Schema.Struct({
temperature: Schema.Number,
condition: Schema.String
})
})
const toolkit = Toolkit.make(GetCurrentTime, GetWeather)
const ready = toolkit.pipe(Effect.provide(toolkit.toLayer({
GetCurrentTime: () => Effect.succeed(0),
get_weather: () => Effect.succeed({ temperature: 20, condition: "clear" })
})))
Object.keys((await Effect.runPromise(ready)).tools) // => ["GetCurrentTime", "get_weather"]

@stability ― unstable

@category ― constructors

@since ― 4.0.0

make
(
const Research: SubagentDelegation<"delegate_research_activities", 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>;
}, ... 5 more ..., "error"> & {
...;
}
Research
.
SubagentDelegation<"delegate_research_activities", Struct<{ readonly city: String; readonly focus: String; }>, Struct<{ readonly activities: $Array<String>; readonly researchNotes: String; }>, ... 7 more ..., "error">.tool: Tool<"delegate_research_activities", {
readonly parameters: Schema.Struct<{
readonly city: Schema.String;
readonly focus: Schema.String;
}>;
readonly success: Schema.Struct<{
readonly output: Schema.Struct<{
readonly activities: Schema.$Array<Schema.String>;
readonly researchNotes: Schema.String;
}>;
readonly budgetExhausted: Schema.Boolean;
}>;
readonly failure: SubagentToolFailure<...>;
readonly failureMode: "error";
}, AgentSpawner | ... 1 more ... | SubagentDurability>

The real Effect AI Tool to include in the parent Toolkit (SUB-001).

tool
),
DefinitionOptions<Struct<{ readonly city: String; }>, Struct<{ readonly itinerary: $Array<String>; }>, string, Toolkit<{ readonly delegate_research_activities: Tool<...>; }>, 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
: 2,
maxDuration?: Input | undefined

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

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

The parent sees the child’s output as the tool’s answer:

{
"output": {
"activities": ["Riverside walk", "Food market"],
"researchNotes": "Both fit a day of food and walking."
},
"budgetExhausted": false
}

delegation-main.ts
import {
import NodeRuntime
NodeRuntime
} from "@effect/platform-node";
import {
import Console
Console
,
import Effect
Effect
} from "effect";
import {
const program: Effect.Effect<{
readonly output: {
readonly itinerary: readonly string[];
};
readonly finishReason: "completed" | "model-stop" | "budget-exhausted";
readonly turns: number;
readonly threadId: string & Brand<"@effect-agent/core/ThreadId">;
readonly runId: string & Brand<"@effect-agent/core/RunId">;
readonly exhausted?: "tokens" | "tool-calls" | "turns" | undefined;
readonly runDisposition?: Json | undefined;
readonly usage?: RunTotals | undefined;
readonly delegatedUsage?: RunTotals | undefined;
}, AgentRuntimeFailure<Definition<Struct<{
readonly city: String;
}>, ... 5 more ..., undefined> & {
...;
}, never, never> | ConfigError, never>
program
} from "./delegation-live.ts";
import NodeRuntime
NodeRuntime
.
const runMain: <AgentRuntimeFailure<Definition<Struct<{
readonly city: String;
}>, Struct<{
readonly itinerary: $Array<String>;
}>, string, Toolkit<{
readonly delegate_research_activities: Tool<"delegate_research_activities", {
readonly parameters: Struct<{
readonly city: String;
readonly focus: String;
}>;
readonly success: Struct<{
readonly output: Struct<{
readonly activities: $Array<String>;
readonly researchNotes: String;
}>;
readonly budgetExhausted: Boolean;
}>;
readonly failure: SubagentToolFailure<...>;
readonly failureMode: "error";
}, AgentSpawner | ... 1 more ... | SubagentDurability>;
}>, undefined, undefined, undefined> & {
...;
}, never, never> | ConfigError, {
...;
}>(effect: Effect.Effect<...>, 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
(
const program: Effect.Effect<{
readonly output: {
readonly itinerary: readonly string[];
};
readonly finishReason: "completed" | "model-stop" | "budget-exhausted";
readonly turns: number;
readonly threadId: string & Brand<"@effect-agent/core/ThreadId">;
readonly runId: string & Brand<"@effect-agent/core/RunId">;
readonly exhausted?: "tokens" | "tool-calls" | "turns" | undefined;
readonly runDisposition?: Json | undefined;
readonly usage?: RunTotals | undefined;
readonly delegatedUsage?: RunTotals | undefined;
}, AgentRuntimeFailure<Definition<Struct<{
readonly city: String;
}>, ... 5 more ..., undefined> & {
...;
}, never, never> | ConfigError, never>
program
.
Pipeable.pipe<Effect.Effect<{
readonly output: {
readonly itinerary: readonly string[];
};
readonly finishReason: "completed" | "model-stop" | "budget-exhausted";
readonly turns: number;
readonly threadId: string & Brand<"@effect-agent/core/ThreadId">;
readonly runId: string & Brand<"@effect-agent/core/RunId">;
readonly exhausted?: "tokens" | "tool-calls" | "turns" | undefined;
readonly runDisposition?: Json | undefined;
readonly usage?: RunTotals | undefined;
readonly delegatedUsage?: RunTotals | undefined;
}, AgentRuntimeFailure<Definition<Struct<...>, ... 5 more ..., undefined> & {
...;
}, never, never> | ConfigError, never>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const tap: <{
readonly output: {
readonly itinerary: readonly string[];
};
readonly finishReason: "completed" | "model-stop" | "budget-exhausted";
readonly turns: number;
readonly threadId: string & Brand<"@effect-agent/core/ThreadId">;
readonly runId: string & Brand<"@effect-agent/core/RunId">;
readonly exhausted?: "tokens" | "tool-calls" | "turns" | undefined;
readonly runDisposition?: Json | undefined;
readonly usage?: RunTotals | undefined;
readonly delegatedUsage?: RunTotals | undefined;
}, void, never, never>(f: (a: {
readonly output: {
readonly itinerary: readonly string[];
};
readonly finishReason: "completed" | "model-stop" | "budget-exhausted";
readonly turns: number;
readonly threadId: string & Brand<"@effect-agent/core/ThreadId">;
readonly runId: string & Brand<"@effect-agent/core/RunId">;
readonly exhausted?: "tokens" | "tool-calls" | "turns" | undefined;
readonly runDisposition?: Json | undefined;
readonly usage?: RunTotals | undefined;
readonly delegatedUsage?: RunTotals | undefined;
}) => Effect.Effect<...>) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+3 overloads)

Runs a side effect with the result of an effect without changing the original value.

When to use

Use when you need to run an effectful observation, such as logging or tracking, while passing the original success value to the next step.

Details

tap works similarly to flatMap, but it ignores the result of the function passed to it. The value from the previous effect remains available for the next part of the chain. Note that if the side effect fails, the entire chain will fail too.

Example (Logging a step in a pipeline)

import { Data, Effect, pipe } from "effect"
const output: Array<unknown> = []
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const finalAmount = pipe(
fetchTransactionAmount,
// Log the fetched transaction amount
Effect.tap((amount) => Effect.sync(() => { output.push(`Apply a discount to: ${amount}`) })),
// `amount` is still available!
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
void output.push(await Effect.runPromise(finalAmount))
output // => ["Apply a discount to: 100", 95]

@category ― sequencing

@since ― 2.0.0

tap
(({
output: {
readonly itinerary: readonly string[];
}
output
}) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

Logs a general-purpose message to the console.

Example (Writing log messages)

import { Console, Effect } from "effect"
const messages: Array<ReadonlyArray<unknown>> = []
const testConsole: Console.Console = Object.assign(Object.create(console), {
log: (...args: ReadonlyArray<unknown>) => messages.push(args)
})
const program = Effect.gen(function*() {
yield* Console.log("Hello, world!")
yield* Console.log("User data:", { name: "John", age: 30 })
yield* Console.log("Processing", 42, "items")
})
Effect.runSync(Effect.provideService(program, Console.Console, testConsole))
const expected = [
["Hello, world!"],
["User data:", { name: "John", age: 30 }],
["Processing", 42, "items"]
]
messages // => expected

@category ― accessors

@since ― 2.0.0

log
(
output: {
readonly itinerary: readonly string[];
}
output
))));
export OPENAI_API_KEY="your-api-key"
node --experimental-transform-types delegation-main.ts

The child shares the parent’s Scope. Interruption stops both; a process restart loses active execution. Use durable attached when that work needs recovery. Stored history alone does not make execution durable.

To expose only selected findings, add success and projectResult to the declaration. You can also supply explicit child limits and map failures to an application error. The mapping example shows all three customizations after the minimal setup above.

One delegation counts as one parent tool call. The child consumes its own reserved allowance. Set failureMode: "return" to give expected child failures to the parent model as data; defects and interruption retain their Effect meaning.

See budgets and permissions, or switch to background workers so the parent can continue while children work.