Skip to content

Build agents

Context management

Each model request resends its context. Long runs can spend most of their budget rereading old tool results or exceed the model’s context window. Yielded Agent bounds tool output, reports the remaining budget, and compacts old context. Final-answer policy can grant one constrained turn after turn, tool call, or token exhaustion.

Set these limits from the model and workload. The provider does not supply them:

AgentPolicy.make({
maxTurns: 12,
maxToolCalls: 24,
maxDuration: "5 minutes",
toolConcurrency: 4,
tokenBudget: 200_000,
completionReserveTokens: 32_000,
costBudgetMicrousd: 2_000_000,
contextTokenLimit: 150_000,
toolResultBounds: ToolResultBounds.make({ maxBytes: 50 * 1024 }),
runStatus: "off",
compaction: CompactionPolicy.make({ keepRecentTokens: 20_000 }),
onExhaustion: "final-answer",
});

tokenBudget counts cumulative input and output across the run. costBudgetMicrousd uses the installed cost estimator and cache-split usage. contextTokenLimit bounds the live context for one call. Set it below the model window so output and compaction have room.

At run start, the runtime evaluates instructions and the definition’s optional inputPrompt. Without inputPrompt, the model receives the full encoded input as a JSON user message.

Before each turn, the durable runtime commits the previous completed Tool batch, then RunContextPreparation.hook.prepare transforms the source prompt. A host resolving the next model from successful Tool receipts can therefore read their canonical records during preparation. The engine compacts the prepared history, then loads optional references through RunContextPreparation.transientContext.load. If the references exceed the remaining budget, the engine can compact canonical history further while keeping the same reference snapshot. It appends the references to the compacted view. OpenAI, xAI Responses, and native adapters advertising support for system messages in history preserve chronological guidance, with the output contract after the initial system block. Changing late instructions therefore leaves earlier history reusable. The engine resolves this capability from the selected model on each call. Other adapters, including the pinned Anthropic adapter, group systems before the conversation. Derived run status follows this projection, which preserves stored history and compaction boundaries. See prompt caching for provider differences. Compaction summaries never receive transient references. Durable recovery rebuilds the committed model view before applying prompt preparation; a transient loader receives the current Attempt’s official source, Thread ID, Run ID, Turn ID, and Turn number.

Each turn releases its response trace and prepared context before the next turn starts. Official history and detached event replay retain their own records. Resources acquired by beforeTurn or context.prepare also close at that turn boundary, including on failure or interruption. Acquire resources needed across turns in a surrounding run Layer or Scope.

To add application instructions to each request:

import {
class RunContextPreparation

Host-owned model-context preparation, independent of action-time Tool authorization.

Runs use this service when provided; durable coordinators capture it while their runtime Layer is acquired. Implementations acquire dependencies in their Layer and preserve the declared error tags. Layer acquisition failures belong to the providing Effect, not this union. Without this service, Runs apply no host context loading. Provide ContextCompactor separately to select native compaction; neither service replaces RunToolAuthorization.

RunContextPreparation
, type
interface RunContextHook<Error = never, Requirements = never>

Model-only prompt transformation. Compaction requires a safely mapped original instruction/input block and content-equivalent covered prefixes across Turns. Incompatible changes fail with CompactionError before compaction or model I/O.

RunContextHook
} from "@yielded/agent/run-options";
import {
import Effect
Effect
,
import Layer
Layer
} from "effect";
import {
import Prompt
Prompt
} from "effect/ai";
export const
const metricContext: RunContextHook<never, never>
metricContext
:
interface RunContextHook<Error = never, Requirements = never>

Model-only prompt transformation. Compaction requires a safely mapped original instruction/input block and content-equivalent covered prefixes across Turns. Incompatible changes fail with CompactionError before compaction or model I/O.

RunContextHook
= {
RunContextHook<never, never>.prepare: (request: RunContextRequest) => Effect.Effect<PreparedRunContext, never, never>

Resources acquired by prepare belong to the current Turn and close before the next Turn starts. Acquire resources shared across Turns in a surrounding Run Layer or Scope instead.

prepare
: ({
source: Prompt.Prompt

Official engine history for this Attempt before context preparation. The engine never replaces or mutates this value. Durable prompt reconstruction can produce a different model-visible basis afterward, so adapters that need canonical retrieval should key it by the supplied Run identities rather than infer durable state from this Prompt alone.

source
}) =>
import Effect
Effect
.
const succeed: <{
prompt: Prompt.Prompt;
}>(value: {
prompt: Prompt.Prompt;
}) => Effect.Effect<{
prompt: Prompt.Prompt;
}, 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
({
prompt: Prompt.Prompt
prompt
:
import Prompt
Prompt
.
const concat: (self: Prompt.Prompt, input: Prompt.RawInput) => Prompt.Prompt (+1 overload)

Concatenates a prompt with additional raw input by concatenating messages.

Details

The returned prompt contains all messages from the original prompt followed by the provided raw input, preserving message order.

Example (Concatenating prompts)

import { Prompt } from "effect/ai"
const systemPrompt = Prompt.make([{
role: "system",
content: "You are a helpful assistant."
}])
const merged = Prompt.concat(systemPrompt, "Hello, world!")
merged.content.map((message) => message.role) // => ["system", "user"]

@stability ― unstable

@category ― combinators

@since ― 4.0.0

concat
(
import Prompt
Prompt
.
const make: (input: Prompt.RawInput) => Prompt.Prompt

Creates a Prompt from an input.

Details

This is the primary constructor for creating prompts, supporting multiple input formats for convenience and flexibility.

Example (Creating prompts from inputs)

import { Prompt } from "effect/ai"
// From string - creates a user message
const textPrompt = Prompt.make("Hello, how are you?")
// From messages array
const structuredPrompt = Prompt.make([
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: [{ type: "text", text: "Hi!" }] }
])
const copiedPrompt = Prompt.make(Prompt.empty)
const result = [textPrompt.content[0].role, structuredPrompt.content.length, copiedPrompt.content.length] // => ["user", 2, 0]

@stability ― unstable

@category ― constructors

@since ― 4.0.0

make
([{
BaseMessageEncoded<"system", SystemMessageOptions>.role: "system"

The role of the message participant.

role
: "system",
SystemMessageEncoded.content: string

The system instruction or context as plain text.

content
: "Use metric units in your answer." }]),
source: Prompt.Prompt

Official engine history for this Attempt before context preparation. The engine never replaces or mutates this value. Durable prompt reconstruction can produce a different model-visible basis afterward, so adapters that need canonical retrieval should key it by the supplied Run identities rather than infer durable state from this Prompt alone.

source
,
),
}),
};
export const
const MetricContextLive: Layer.Layer<RunContextPreparation, never, never>
MetricContextLive
=
import Layer
Layer
.
const succeed: <RunContextPreparation, {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}>(service: Key<RunContextPreparation, {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}>, resource: {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}) => 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
(
class RunContextPreparation

Host-owned model-context preparation, independent of action-time Tool authorization.

Runs use this service when provided; durable coordinators capture it while their runtime Layer is acquired. Implementations acquire dependencies in their Layer and preserve the declared error tags. Layer acquisition failures belong to the providing Effect, not this union. Without this service, Runs apply no host context loading. Provide ContextCompactor separately to select native compaction; neither service replaces RunToolAuthorization.

RunContextPreparation
, {
hook?: RunContextHook<RunContextPreparationError, never> | undefined

Optional transformation of the model-visible prompt.

hook
:
const metricContext: RunContextHook<never, never>
metricContext
});

Provide MetricContextLive to AgentRuntime.run or start with Effect.provide, or to AgentRuntime.stream with Stream.provide. With start, provide the Layer around the whole scoped workflow, including awaiting the handle, so its resources remain available until the detached Run finishes. For durable execution, install MetricContextLive when configuring the runtime. Transforms change the model prompt, not stored input or history. Use inputPrompt to choose which input fields the model sees.

Prepared and transient context use full provider requests, bypassing native response-ID reuse so discarded material cannot remain in a provider-held conversation. Prompt caching still applies to matching prefixes. A transient user-message suffix can move OpenAI’s implicit cache-write boundary past the retained history, even when the reference text stays identical. To reuse that history, place native explicit cache markers in the stable prefix through context preparation; keep untrusted references in user messages. Append changing trusted system guidance after source; supported chronological adapters keep it after the retained history. Prepending changing content still invalidates the prefix. Anthropic needs both a capable upstream adapter and a supported model; its pinned adapter retains grouped systems. Place native Anthropic cache markers before changing transient context rather than relying on automatic placement after that suffix. See provider cache settings for the release limitation and xAI routing configuration.

Token-limited calls estimate their current prepared messages. The engine reuses default counts within each prepared prompt and recomputes them after the next context preparation. Calls without contextTokenLimit, resolved model capacity, or tokenBudget skip admission estimates; Context Tools still obtain their live-context estimate on demand. For nondurable compaction, retain the original instruction/input messages or an unambiguous, content-equivalent ordering of them. The engine rejects compaction with CompactionError when that block cannot be mapped safely. Original message identities disambiguate repeated instructions or input text. After compaction, preparation must preserve the content and order of the covered prefix. Rebuilding equivalent messages is supported; replacing, inserting into, or reordering that prefix fails before another model request. Durable reconstruction keeps its canonical coverage checks at commit time.

A host that routes between models can return modelCall from prepare. Capture one resolved provider configuration and build both its native Model Layer and ModelCallContext from that configuration. The engine acquires the Layer once for the turn and reuses it for admission, dispatch, usage accounting, and bounded overflow recovery. Its resources close at the turn boundary, including when preparation or admission fails. A separately configured compaction model retains its own binding. A summary using the captured model must also fit its input allowance; an oversized summary fails with CompactionError before provider I/O.

ModelCallContext carries the model’s full contextCapacity, optional maxInputTokens, its configured outputReserveTokens, and uncountedOverheadTokens. The effective input allowance is the minimum of the definition’s optional context limit, the model’s input limit, and context capacity minus output reserve, less uncounted overhead. An exhausted allowance fails with ContextBudgetError before dispatch. Capacity remains an estimate when exact token counting is unavailable.

The engine counts the prepared prompt, transient references, output contract, run status, and the native Tool schemas dispatched for the call. Supply the provider’s native toolSchemaTransformer, such as toCodecOpenAI from effect/ai/OpenAiStructuredOutput, to include its schema conversion. Reserve additional framing or image costs only when they are absent from those estimates. Do not subtract prompt text or Tool schemas again as overhead. The output reserve must match the selected provider’s generation allowance; completionReserveTokens instead reserves cumulative Run budget for delivery and does not provide this per-call allowance.

Durable hosts must capture resolved model settings in their own schema-validated admission data and restore committed routing changes before preparation. Returning a Layer does not persist its configuration or register a different Agent revision. Keep the original registration available for already-admitted Runs.

Hosts may also return rollover: {} to reset prior history below the capacity threshold. The engine selects the prefix before the current Run’s protected instructions and input; an empty or already covered prefix is a no-op, including after recovery. For an explicit selection, return rollover: { through, handoff? }, where through is an exclusive source-message boundary. Leave that prefix intact in the prepared prompt. The engine maps it to complete canonical records and commits ordinary native rollover. Protected input and pending Tool pairs cannot be discarded. This does not manufacture a model Tool Call, start another Run, or reset its deadline and usage. Do not return a host rollover while a successful new_context request is already pending.

Memory.recall turns readable, application-selected sources into a bounded transient model view. The framework does not own a memory database, write recalled material, or build an embedding index. An Agent that does not need context loading requires no context service, memory reader, or store. RunContextPreparationPassthrough remains available to explicitly disable inherited context preparation.

Each reader returns MemoryLookup. Found carries ranked MemoryPassage values. NoMatch is a successful empty result. Unavailable and InsufficientFreshness remain distinct in the returned source outcomes. An unavailable or stale essential: true source fails recall; an essential source whose matching passages cannot fit also fails. NoMatch remains successful even for an essential source. Expected reader failures propagate unless the reader deliberately maps them to one of the lookup outcomes.

A passage points back to its authoritative source ID, locator, and known revision. Its attribution records the speaker, observers, original activity time, and the application’s interpretation. recordedAt says when the application recorded the content, while extractedAt says when it made this passage. Neither substitutes for activityAt; use null when the original activity time is unknown.

This source reads a known Markdown note without a store or adapter:

import {
import Memory
Memory
} from "@yielded/agent";
import {
class MemoryAttribution

Evidence of who said something, where, when, and who observed it. Interpretation is consumer-defined, for example "proposal" or "inference". Repeated references retain the same originId; they are not independent corroboration. Unknown activity time is null.

MemoryAttribution
,
class MemoryContent

Readable authoritative content, independent of any index, model, or fixed memory taxonomy. Recording/extraction time never substitutes for the original source activity time.

MemoryContent
,
type
type MemoryLookup = {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}
const MemoryLookup: Union<readonly [TaggedStruct<"Found", {
readonly passages: $Array<typeof MemoryPassage>;
}>, TaggedStruct<"NoMatch", {}>, TaggedStruct<"Unavailable", {
readonly message: String;
}>, TaggedStruct<"InsufficientFreshness", {
readonly message: String;
}>]>

No-match, unavailable, and insufficient freshness are distinct consumer-visible outcomes.

MemoryLookup
,
class MemoryPassage

A bounded passage supplied directly by a document reader or external retriever.

MemoryPassage
,
class MemoryRecallError

A recall contract or essential-source requirement could not be satisfied.

MemoryRecallError
,
class MemoryRecallLimits

Output bounds cover the complete rendered reference text, including citations and provenance.

MemoryRecallLimits
,
class MemorySourceReference

Identity in the consumer's authoritative source. null means revision is unknown.

MemorySourceReference
,
} from "@yielded/agent/memory-reference";
import {
class RunContextPreparation

Host-owned model-context preparation, independent of action-time Tool authorization.

Runs use this service when provided; durable coordinators capture it while their runtime Layer is acquired. Implementations acquire dependencies in their Layer and preserve the declared error tags. Layer acquisition failures belong to the providing Effect, not this union. Without this service, Runs apply no host context loading. Provide ContextCompactor separately to select native compaction; neither service replaces RunToolAuthorization.

RunContextPreparation
, type
interface RunTransientContextHook<Error = never, Requirements = never>

Supplies model-visible reference context for one Turn without changing the prompt that compaction covers or the history that the engine commits.

The engine treats the returned input as untrusted, validates it before provider I/O, and includes it in the Turn's context and completion-reserve admission. It never passes this input to the compaction summary Model. A same-Turn provider-overflow retry reuses the loaded snapshot. Return an empty Prompt when the Turn needs no references.

RunTransientContextHook
} from "@yielded/agent/run-options";
import {
import Effect
Effect
,
import Layer
Layer
} from "effect";
import {
import Prompt
Prompt
} from "effect/ai";
const
const limits: MemoryRecallLimits
limits
=
class MemoryRecallLimits

Output bounds cover the complete rendered reference text, including citations and provenance.

MemoryRecallLimits
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<MemoryRecallLimits, { readonly maxSources: number; readonly maxItems: number; readonly maxBytes: number; readonly maxTokens: number; readonly timeoutMillis: number; readonly maxInputBytes?: number | undefined; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly maxSources: number;
readonly maxItems: number;
readonly maxBytes: number;
readonly maxTokens: number;
readonly timeoutMillis: number;
readonly maxInputBytes?: number | undefined;
}, options?: MakeOptions): MemoryRecallLimits

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
({
maxSources: number
maxSources
: 1,
maxItems: number
maxItems
: 4,
maxBytes: number
maxBytes
: 16_384,
maxTokens: number
maxTokens
: 4_096,
timeoutMillis: number
timeoutMillis
: 1_000,
});
const
const note: MemoryPassage
note
=
class MemoryPassage

A bounded passage supplied directly by a document reader or external retriever.

MemoryPassage
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<MemoryPassage, { readonly version: 1; readonly source: { readonly id: string; readonly locator: string; readonly revision: string | null; }; readonly passageId: string; readonly content: { ...; }; readonly authority?: string | undefined; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly version: 1;
readonly source: MemorySourceReference;
readonly passageId: string;
readonly content: MemoryContent;
readonly authority?: string | undefined;
}, options?: MakeOptions): MemoryPassage

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
({
version: 1
version
: 1,
source: MemorySourceReference
source
:
class MemorySourceReference

Identity in the consumer's authoritative source. null means revision is unknown.

MemorySourceReference
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<MemorySourceReference, { readonly id: string; readonly locator: string; readonly revision: string | null; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly id: string;
readonly locator: string;
readonly revision: string | null;
}, options?: MakeOptions): MemorySourceReference

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
({
id: string
id
: "project-notes",
locator: string
locator
: "file:///workspace/notes/queue.md",
revision: string | null
revision
: "sha256:8d31",
}),
passageId: string
passageId
: "retry-policy",
content: MemoryContent
content
:
class MemoryContent

Readable authoritative content, independent of any index, model, or fixed memory taxonomy. Recording/extraction time never substitutes for the original source activity time.

MemoryContent
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<MemoryContent, { readonly text: string; readonly attributions: readonly { readonly originId: string; readonly speaker: string; readonly observers: readonly string[]; readonly locator: string; readonly activityAt: number | null; readonly interpretation: string; }[]; readonly metadata: { ...; }; readonly recordedAt: number; readonly extractedAt?: number | undefined; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly text: string;
readonly attributions: readonly MemoryAttribution[];
readonly metadata: {
readonly [x: string]: unknown;
};
readonly recordedAt: number;
readonly extractedAt?: number | undefined;
}, options?: MakeOptions): MemoryContent

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
({
text: string
text
: "# Retry policy\nUse a bounded queue and preserve failed work.",
attributions: readonly MemoryAttribution[]
attributions
: [
class MemoryAttribution

Evidence of who said something, where, when, and who observed it. Interpretation is consumer-defined, for example "proposal" or "inference". Repeated references retain the same originId; they are not independent corroboration. Unknown activity time is null.

MemoryAttribution
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<MemoryAttribution, { readonly originId: string; readonly speaker: string; readonly observers: readonly string[]; readonly locator: string; readonly activityAt: number | null; readonly interpretation: string; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly originId: string;
readonly speaker: string;
readonly observers: readonly string[];
readonly locator: string;
readonly activityAt: number | null;
readonly interpretation: string;
}, options?: MakeOptions): MemoryAttribution

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
({
originId: string
originId
: "meeting:2026-08-28:queue",
speaker: string
speaker
: "Dan",
observers: readonly string[]
observers
: ["Chad"],
locator: string
locator
: "meeting://engineering/2026-08-28#queue",
activityAt: number | null
activityAt
: 1_777_334_400_000,
interpretation: string
interpretation
: "proposal awaiting review",
}),
],
metadata: {
readonly [x: string]: unknown;
}
metadata
: {
format: string
format
: "markdown" },
recordedAt: number
recordedAt
: 1_777_420_800_000,
extractedAt?: number | undefined
extractedAt
: 1_777_420_801_000,
}),
});
const
const lookup: {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}
lookup
:
type MemoryLookup = {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}

No-match, unavailable, and insufficient freshness are distinct consumer-visible outcomes.

MemoryLookup
= {
_tag: "Found"
_tag
: "Found",
passages: readonly MemoryPassage[]
passages
: [
const note: MemoryPassage
note
] };
export const
const projectNotes: RunTransientContextHook<MemoryRecallError, never>
projectNotes
:
interface RunTransientContextHook<Error = never, Requirements = never>

Supplies model-visible reference context for one Turn without changing the prompt that compaction covers or the history that the engine commits.

The engine treats the returned input as untrusted, validates it before provider I/O, and includes it in the Turn's context and completion-reserve admission. It never passes this input to the compaction summary Model. A same-Turn provider-overflow retry reuses the loaded snapshot. Return an empty Prompt when the Turn needs no references.

RunTransientContextHook
<
class MemoryRecallError

A recall contract or essential-source requirement could not be satisfied.

MemoryRecallError
> = {
RunTransientContextHook<MemoryRecallError, never>.load: (request: RunContextRequest) => Effect.Effect<Prompt.RawInput, MemoryRecallError, never>
load
: () =>
import Memory
Memory
.
recall<never, never>(sources: readonly Memory.MemoryRecallSource<never, never>[], limits: MemoryRecallLimits, estimateTokens?: ((text: string) => number) | undefined): Effect.Effect<Memory.RecalledMemory, MemoryRecallError, never>
export recall

Read in declaration/ranking order and retain whole passages that fit. Essential sources must have a represented passage when they return matches, including exact duplicates. Explicit passage authorities qualify source identity across readers; absent authority is local to the reader declaration. Raw authorities stay in host passages, never rendered text. maxSources bounds both reader declarations and authority-qualified selected sources. No-match is successful even when essential. Optional unavailable/stale sources remain visible in outcomes. Nothing is cached. Admitted conflicting identities are rejected even when an earlier passage does not fit the output budget. maxInputBytes bounds cumulative JSON passage encodings before selection or identity retention; its default is 16 MiB. Exhaustion stops validation and returns no partial context. Reader-owned allocation and result decoding precede that input bound.

The default estimate is one token per UTF-8 byte. Supply the selected model's tokenizer for tighter selection. The engine independently enforces its full per-call context budget. The deadline owns a Scope, so temporary reader resources finalize on every exit path.

recall
(
[{
MemoryRecallSource<E = never, R = never>.id: string
id
: "project-notes",
MemoryRecallSource<E = never, R = never>.essential: boolean
essential
: true,
MemoryRecallSource<never, never>.read: Effect.Effect<{
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}, never, never>
read
:
import Effect
Effect
.
const succeed: <{
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
}>(value: {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
}) => Effect.Effect<{
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
}, 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 lookup: {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
}
lookup
) }],
const limits: MemoryRecallLimits
limits
,
).
Pipeable.pipe<Effect.Effect<Memory.RecalledMemory, MemoryRecallError, never>, Effect.Effect<Prompt.Prompt, MemoryRecallError, never>>(this: Effect.Effect<Memory.RecalledMemory, MemoryRecallError, never>, ab: (_: Effect.Effect<Memory.RecalledMemory, MemoryRecallError, never>) => Effect.Effect<Prompt.Prompt, MemoryRecallError, never>): Effect.Effect<Prompt.Prompt, MemoryRecallError, never> (+21 overloads)
pipe
(
import Effect
Effect
.
const map: <Memory.RecalledMemory, Prompt.Prompt>(f: (a: Memory.RecalledMemory) => Prompt.Prompt) => <E, R>(self: Effect.Effect<Memory.RecalledMemory, E, R>) => Effect.Effect<Prompt.Prompt, E, R> (+1 overload)

Transforms the value inside an effect by applying a function to it.

When to use

Use to transform an effect's success value with a function that returns a plain value, producing a new effect without changing the original effect's typed error or context requirements.

Details

map takes a function and applies it to the value contained within an effect, creating a new effect with the transformed value.

It's important to note that effects are immutable, meaning that the original effect is not modified. Instead, a new effect is returned with the updated value.

Example (Choosing map syntax variants)

import { Effect, pipe } from "effect"
const output: Array<unknown> = []
const myEffect = Effect.succeed(1)
const transformation = (n: number) => n + 1
const mappedWithPipe = pipe(myEffect, Effect.map(transformation))
const mappedWithDataFirst = Effect.map(myEffect, transformation)
const mappedWithMethod = myEffect.pipe(Effect.map(transformation))
void output.push(Effect.runSync(Effect.all([
mappedWithPipe,
mappedWithDataFirst,
mappedWithMethod
])))
output // => [[2, 2, 2]]

Example (Adding a service charge)

import { Effect, pipe } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const finalAmount = pipe(
fetchTransactionAmount,
Effect.map(addServiceCharge)
)
await Effect.runPromise(finalAmount) // => 101

@see ― mapError for a version that operates on the error channel.

@see ― mapBoth for a version that operates on both channels.

@see ― flatMap or andThen for a version that can return a new effect.

@category ― mapping

@since ― 2.0.0

map
((
recalled: Memory.RecalledMemory
recalled
) =>
recalled: Memory.RecalledMemory
recalled
.
text: string
text
=== ""
?
import Prompt
Prompt
.
const empty: Prompt.Prompt

An empty prompt with no messages.

Example (Creating an empty prompt)

import { Prompt } from "effect/ai"
const emptyPrompt = Prompt.empty
emptyPrompt.content // => []

@stability ― unstable

@category ― constructors

@since ― 4.0.0

empty
:
import Prompt
Prompt
.
const make: (input: Prompt.RawInput) => Prompt.Prompt

Creates a Prompt from an input.

Details

This is the primary constructor for creating prompts, supporting multiple input formats for convenience and flexibility.

Example (Creating prompts from inputs)

import { Prompt } from "effect/ai"
// From string - creates a user message
const textPrompt = Prompt.make("Hello, how are you?")
// From messages array
const structuredPrompt = Prompt.make([
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: [{ type: "text", text: "Hi!" }] }
])
const copiedPrompt = Prompt.make(Prompt.empty)
const result = [textPrompt.content[0].role, structuredPrompt.content.length, copiedPrompt.content.length] // => ["user", 2, 0]

@stability ― unstable

@category ― constructors

@since ― 4.0.0

make
([{
BaseMessageEncoded<"user", UserMessageOptions>.role: "user"

The role of the message participant.

role
: "user",
UserMessageEncoded.content: string | readonly Prompt.UserMessagePartEncoded[]

Array of content parts that make up the user's message.

content
:
recalled: Memory.RecalledMemory
recalled
.
text: string
text
}]),
),
),
};
export const
const ProjectContextLive: Layer.Layer<RunContextPreparation, never, never>
ProjectContextLive
=
import Layer
Layer
.
const succeed: <RunContextPreparation, {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}>(service: Key<RunContextPreparation, {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}>, resource: {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}) => 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
(
class RunContextPreparation

Host-owned model-context preparation, independent of action-time Tool authorization.

Runs use this service when provided; durable coordinators capture it while their runtime Layer is acquired. Implementations acquire dependencies in their Layer and preserve the declared error tags. Layer acquisition failures belong to the providing Effect, not this union. Without this service, Runs apply no host context loading. Provide ContextCompactor separately to select native compaction; neither service replaces RunToolAuthorization.

RunContextPreparation
, {
transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined

Optional per-Turn reference context, excluded from history and compaction coverage.

transientContext
:
const projectNotes: RunTransientContextHook<MemoryRecallError, never>
projectNotes
,
});

Provide ProjectContextLive to the run. With an existing agent and input:

const runnable = AgentRuntime.run(agent, input).pipe(
Effect.provide(ProjectContextLive),
Effect.catchTags({
MemoryRecallError: handleRecallFailure,
CompactionError: handleCompactionFailure,
}),
);

The service declares AgentInputError | MemoryRecallError | CompactionError; method failures retain their original tags and fields. RunContextPreparationError names that type union, not a wrapper class. Effect.provide adds any errors from acquiring the Layer separately. Adapters handle backend-specific failures or translate them into this contract. Providing a Layer does not add undeclared service-method errors to a program’s error type.

For an application-specific error or requirement channel, the existing generic RunOptions.context and RunOptions.transientContext hooks override the corresponding service fields for that Run. Other service fields remain active. Attached children inherit the host context service, not the parent’s per-Run overrides.

Memory.recall renders positional memory:N reference IDs and provenance for that one result. RecalledMemory.outcomes separately reports what happened at each source. The rendered envelope and every citation remain untrusted model input. Validate model claims against RecalledMemory.passages before presenting them as sourced facts.

Source IDs are local to an authority. A host can set MemoryPassage.authority to share that authority across readers; passages without it are scoped to their reader declaration’s id. Deduplication, conflict detection, and the selected-source limit use authority-qualified IDs. Two independent authorities can therefore both contain profile at revision 1 without being merged or rejected. Direct readers must explicitly share an authority to deduplicate across reader declarations. Authority is an identity boundary, not an authorization grant.

The rendered text substitutes positional memory-authority:N labels for private authority values. These labels are local to the result and qualify its evidence origins; different labels alone do not prove independent corroboration. RecalledMemory.passages retains explicit authority for host-side composition and validation. Use RecalledMemory.text for model input, not a raw serialization of those host passages.

MemoryRecallLimits.maxInputBytes separately bounds the aggregate UTF-8 JSON passage encodings considered by one call, including omitted and duplicate candidates. It defaults to 16 MiB and can be set up to 64 MiB. Exceeding it returns a typed budget error before retaining that candidate’s identity or encoding, even when the source is optional. Reader allocation, result decoding, and one candidate’s serialization occur before this check; it is not a whole-process heap limit. Input-budget exhaustion stops validation and returns no partial context. Within admitted input, conflicting known-revision identities fail even when an earlier passage exceeds the output budget. Identity and conflict checks ignore JSON object member order, including nested metadata, while preserving array order. Equivalent unknown-revision passages within one authority share one citation.

Read an external corpus through an Effect service

Section titled “Read an external corpus through an Effect service”

Keep authorization, credentials, and query policy inside an application service. This contract has one read method; it does not create a durable copy, write to the corpus, or create embeddings:

import {
import Memory
Memory
} from "@yielded/agent";
import {
type
type MemoryLookup = {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}
const MemoryLookup: Union<readonly [TaggedStruct<"Found", {
readonly passages: $Array<typeof MemoryPassage>;
}>, TaggedStruct<"NoMatch", {}>, TaggedStruct<"Unavailable", {
readonly message: String;
}>, TaggedStruct<"InsufficientFreshness", {
readonly message: String;
}>]>

No-match, unavailable, and insufficient freshness are distinct consumer-visible outcomes.

MemoryLookup
,
class MemoryRecallError

A recall contract or essential-source requirement could not be satisfied.

MemoryRecallError
,
class MemoryRecallLimits

Output bounds cover the complete rendered reference text, including citations and provenance.

MemoryRecallLimits
,
} from "@yielded/agent/memory-reference";
import {
class RunContextPreparation

Host-owned model-context preparation, independent of action-time Tool authorization.

Runs use this service when provided; durable coordinators capture it while their runtime Layer is acquired. Implementations acquire dependencies in their Layer and preserve the declared error tags. Layer acquisition failures belong to the providing Effect, not this union. Without this service, Runs apply no host context loading. Provide ContextCompactor separately to select native compaction; neither service replaces RunToolAuthorization.

RunContextPreparation
, type
interface RunTransientContextHook<Error = never, Requirements = never>

Supplies model-visible reference context for one Turn without changing the prompt that compaction covers or the history that the engine commits.

The engine treats the returned input as untrusted, validates it before provider I/O, and includes it in the Turn's context and completion-reserve admission. It never passes this input to the compaction summary Model. A same-Turn provider-overflow retry reuses the loaded snapshot. Return an empty Prompt when the Turn needs no references.

RunTransientContextHook
} from "@yielded/agent/run-options";
import {
import Context
Context
,
import Effect
Effect
,
import Layer
Layer
} from "effect";
import {
import Prompt
Prompt
} from "effect/ai";
class
class ExternalCorpus
ExternalCorpus
extends
import Context
Context
.
const Service: <ExternalCorpus, {
readonly search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>;
}>() => <Identifier, E, R, Args>(id: Identifier, options?: {
readonly make?: ((...args: Args) => Effect.Effect<{
readonly search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>;
}, E, R>) | Effect.Effect<{
readonly search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>;
}, E, R> | undefined;
} | undefined) => Context.ServiceClass<...> & ([...] extends [...] ? unknown : {
...;
}) (+2 overloads)

Creates a Context service key.

When to use

Use when you need to define a context service key for a dependency that must be provided by the surrounding context.

Details

Call Context.Service("Key") for a function-style key, or use the two-stage form Context.Service<Self, Shape>()("Key") for class-style service declarations. The returned key can be yielded as an Effect and passed to Context.make, Context.add, and the Context getter functions.

Gotchas

The string key is the runtime identity of the service. Reusing the same key string for unrelated services makes them occupy the same slot in a Context.

Example (Creating service keys)

import { Context } from "effect"
// Create a simple service
const Database = Context.Service<{
query: (sql: string) => string
}>("Database")
// Create a service class
class Config extends Context.Service<Config, {
port: number
}>()("Config") {}
// Use the services to create contexts
const db = Context.make(Database, {
query: (sql) => `Result: ${sql}`
})
const config = Context.make(Config, { port: 8080 })
Context.get(db, Database).query("SELECT 1") // => "Result: SELECT 1"
Context.get(config, Config).port // => 8080

@see ― Reference for service keys with default values

@category ― services

@since ― 4.0.0

Service
<
class ExternalCorpus
ExternalCorpus
,
{
readonly
search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>
search
: (
query: string
query
: string) =>
import Effect
Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that lazily describes a workflow or job. The workflow requires some context R, and may fail with an error of type E, or succeed with a value of type A.

When to use

Use when you need to represent a lazy, composable workflow that can require services, fail with a typed error, or succeed with a typed value.

Details

Effect values model resourceful interaction with the outside world, including synchronous, asynchronous, concurrent, and parallel interaction. They use a fiber-based concurrency model, with built-in support for scheduling, fine-grained interruption, structured concurrency, and high scalability.

To run an Effect value, you need a Runtime, which is a type that is capable of executing Effect values.

@category ― models

@since ― 2.0.0

Effect
<
type MemoryLookup = {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}

No-match, unavailable, and insufficient freshness are distinct consumer-visible outcomes.

MemoryLookup
,
class MemoryRecallError

A recall contract or essential-source requirement could not be satisfied.

MemoryRecallError
>;
}
>()("app/ExternalCorpus") {}
const
const limits: MemoryRecallLimits
limits
=
class MemoryRecallLimits

Output bounds cover the complete rendered reference text, including citations and provenance.

MemoryRecallLimits
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<MemoryRecallLimits, { readonly maxSources: number; readonly maxItems: number; readonly maxBytes: number; readonly maxTokens: number; readonly timeoutMillis: number; readonly maxInputBytes?: number | undefined; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly maxSources: number;
readonly maxItems: number;
readonly maxBytes: number;
readonly maxTokens: number;
readonly timeoutMillis: number;
readonly maxInputBytes?: number | undefined;
}, options?: MakeOptions): MemoryRecallLimits

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
({
maxSources: number
maxSources
: 8,
maxItems: number
maxItems
: 8,
maxBytes: number
maxBytes
: 32_768,
maxTokens: number
maxTokens
: 8_192,
timeoutMillis: number
timeoutMillis
: 2_000,
});
export const
const ExternalCorpusMemoryLive: Layer.Layer<RunContextPreparation, never, ExternalCorpus>
ExternalCorpusMemoryLive
=
import Layer
Layer
.
const effect: <RunContextPreparation, {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}, never, ExternalCorpus>(service: Context.Key<RunContextPreparation, {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}>, effect: Effect.Effect<...>) => Layer.Layer<...> (+1 overload)

Constructs a layer from an effect that produces a single service.

When to use

Use when you need to construct a Layer-provided service with an Effect, dependencies, or scoped resource acquisition.

Details

This allows you to create a Layer from an Effect that produces a service. The Effect is executed in the scope of the layer, allowing for proper resource management.

Example (Creating a layer from an effect)

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

@see ― effectContext for effectfully providing multiple services

@see ― effectDiscard for running construction work without providing services

@category ― constructors

@since ― 2.0.0

effect
(
class RunContextPreparation

Host-owned model-context preparation, independent of action-time Tool authorization.

Runs use this service when provided; durable coordinators capture it while their runtime Layer is acquired. Implementations acquire dependencies in their Layer and preserve the declared error tags. Layer acquisition failures belong to the providing Effect, not this union. Without this service, Runs apply no host context loading. Provide ContextCompactor separately to select native compaction; neither service replaces RunToolAuthorization.

RunContextPreparation
,
import Effect
Effect
.
const gen: <Effect.Effect<{
readonly search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>;
}, never, ExternalCorpus>, {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}>(f: () => Generator<Effect.Effect<{
readonly search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>;
}, never, ExternalCorpus>, {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to use

Use when you want to write effectful code that looks and behaves like synchronous code, while still handling asynchronous tasks, errors, and complex control flow such as loops and conditions.

Generator functions work similarly to async/await but keep errors, requirements, and interruption in the Effect type. You can yield* values from effects and return the final result at the end.

Example (Sequencing effects with generators)

import { Data, Effect } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function*() {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})
await Effect.runPromise(program) // => "Final amount to charge: 96"

@category ― constructors

@since ― 2.0.0

gen
(function* () {
const
const corpus: {
readonly search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>;
}
corpus
= yield*
class ExternalCorpus
ExternalCorpus
;
const
const transientContext: RunTransientContextHook<MemoryRecallError, never>
transientContext
:
interface RunTransientContextHook<Error = never, Requirements = never>

Supplies model-visible reference context for one Turn without changing the prompt that compaction covers or the history that the engine commits.

The engine treats the returned input as untrusted, validates it before provider I/O, and includes it in the Turn's context and completion-reserve admission. It never passes this input to the compaction summary Model. A same-Turn provider-overflow retry reuses the loaded snapshot. Return an empty Prompt when the Turn needs no references.

RunTransientContextHook
<
class MemoryRecallError

A recall contract or essential-source requirement could not be satisfied.

MemoryRecallError
> = {
RunTransientContextHook<MemoryRecallError, never>.load: (request: RunContextRequest) => Effect.Effect<Prompt.RawInput, MemoryRecallError, never>
load
: () =>
import Memory
Memory
.
recall<MemoryRecallError, never>(sources: readonly Memory.MemoryRecallSource<MemoryRecallError, never>[], limits: MemoryRecallLimits, estimateTokens?: ((text: string) => number) | undefined): Effect.Effect<Memory.RecalledMemory, MemoryRecallError, never>
export recall

Read in declaration/ranking order and retain whole passages that fit. Essential sources must have a represented passage when they return matches, including exact duplicates. Explicit passage authorities qualify source identity across readers; absent authority is local to the reader declaration. Raw authorities stay in host passages, never rendered text. maxSources bounds both reader declarations and authority-qualified selected sources. No-match is successful even when essential. Optional unavailable/stale sources remain visible in outcomes. Nothing is cached. Admitted conflicting identities are rejected even when an earlier passage does not fit the output budget. maxInputBytes bounds cumulative JSON passage encodings before selection or identity retention; its default is 16 MiB. Exhaustion stops validation and returns no partial context. Reader-owned allocation and result decoding precede that input bound.

The default estimate is one token per UTF-8 byte. Supply the selected model's tokenizer for tighter selection. The engine independently enforces its full per-call context budget. The deadline owns a Scope, so temporary reader resources finalize on every exit path.

recall
(
[
{
MemoryRecallSource<E = never, R = never>.id: string
id
: "team-corpus",
MemoryRecallSource<E = never, R = never>.essential: boolean
essential
: false,
MemoryRecallSource<MemoryRecallError, never>.read: Effect.Effect<{
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}, MemoryRecallError, never>
read
:
const corpus: {
readonly search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>;
}
corpus
.
search: (query: string) => Effect.Effect<MemoryLookup, MemoryRecallError>
search
("current queue design"),
},
],
const limits: MemoryRecallLimits
limits
,
).
Pipeable.pipe<Effect.Effect<Memory.RecalledMemory, MemoryRecallError, never>, Effect.Effect<Prompt.Prompt, MemoryRecallError, never>>(this: Effect.Effect<Memory.RecalledMemory, MemoryRecallError, never>, ab: (_: Effect.Effect<Memory.RecalledMemory, MemoryRecallError, never>) => Effect.Effect<Prompt.Prompt, MemoryRecallError, never>): Effect.Effect<Prompt.Prompt, MemoryRecallError, never> (+21 overloads)
pipe
(
import Effect
Effect
.
const map: <Memory.RecalledMemory, Prompt.Prompt>(f: (a: Memory.RecalledMemory) => Prompt.Prompt) => <E, R>(self: Effect.Effect<Memory.RecalledMemory, E, R>) => Effect.Effect<Prompt.Prompt, E, R> (+1 overload)

Transforms the value inside an effect by applying a function to it.

When to use

Use to transform an effect's success value with a function that returns a plain value, producing a new effect without changing the original effect's typed error or context requirements.

Details

map takes a function and applies it to the value contained within an effect, creating a new effect with the transformed value.

It's important to note that effects are immutable, meaning that the original effect is not modified. Instead, a new effect is returned with the updated value.

Example (Choosing map syntax variants)

import { Effect, pipe } from "effect"
const output: Array<unknown> = []
const myEffect = Effect.succeed(1)
const transformation = (n: number) => n + 1
const mappedWithPipe = pipe(myEffect, Effect.map(transformation))
const mappedWithDataFirst = Effect.map(myEffect, transformation)
const mappedWithMethod = myEffect.pipe(Effect.map(transformation))
void output.push(Effect.runSync(Effect.all([
mappedWithPipe,
mappedWithDataFirst,
mappedWithMethod
])))
output // => [[2, 2, 2]]

Example (Adding a service charge)

import { Effect, pipe } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const finalAmount = pipe(
fetchTransactionAmount,
Effect.map(addServiceCharge)
)
await Effect.runPromise(finalAmount) // => 101

@see ― mapError for a version that operates on the error channel.

@see ― mapBoth for a version that operates on both channels.

@see ― flatMap or andThen for a version that can return a new effect.

@category ― mapping

@since ― 2.0.0

map
((
recalled: Memory.RecalledMemory
recalled
) =>
recalled: Memory.RecalledMemory
recalled
.
text: string
text
=== ""
?
import Prompt
Prompt
.
const empty: Prompt.Prompt

An empty prompt with no messages.

Example (Creating an empty prompt)

import { Prompt } from "effect/ai"
const emptyPrompt = Prompt.empty
emptyPrompt.content // => []

@stability ― unstable

@category ― constructors

@since ― 4.0.0

empty
:
import Prompt
Prompt
.
const make: (input: Prompt.RawInput) => Prompt.Prompt

Creates a Prompt from an input.

Details

This is the primary constructor for creating prompts, supporting multiple input formats for convenience and flexibility.

Example (Creating prompts from inputs)

import { Prompt } from "effect/ai"
// From string - creates a user message
const textPrompt = Prompt.make("Hello, how are you?")
// From messages array
const structuredPrompt = Prompt.make([
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: [{ type: "text", text: "Hi!" }] }
])
const copiedPrompt = Prompt.make(Prompt.empty)
const result = [textPrompt.content[0].role, structuredPrompt.content.length, copiedPrompt.content.length] // => ["user", 2, 0]

@stability ― unstable

@category ― constructors

@since ― 4.0.0

make
([{
BaseMessageEncoded<"user", UserMessageOptions>.role: "user"

The role of the message participant.

role
: "user",
UserMessageEncoded.content: string | readonly Prompt.UserMessagePartEncoded[]

Array of content parts that make up the user's message.

content
:
recalled: Memory.RecalledMemory
recalled
.
text: string
text
}]),
),
),
};
return
class RunContextPreparation

Host-owned model-context preparation, independent of action-time Tool authorization.

Runs use this service when provided; durable coordinators capture it while their runtime Layer is acquired. Implementations acquire dependencies in their Layer and preserve the declared error tags. Layer acquisition failures belong to the providing Effect, not this union. Without this service, Runs apply no host context loading. Provide ContextCompactor separately to select native compaction; neither service replaces RunToolAuthorization.

RunContextPreparation
.
Service<RunContextPreparation, { readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined; readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined; }>.of(this: void, self: {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}): {
readonly hook?: RunContextHook<RunContextPreparationError, never> | undefined;
readonly transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined;
}
of
({
transientContext?: RunTransientContextHook<RunContextPreparationError, never> | undefined

Optional per-Turn reference context, excluded from history and compaction coverage.

transientContext
});
}),
);

Provide the application’s ExternalCorpus Layer to ExternalCorpusMemoryLive, then provide that closed Layer to an ephemeral Run or to DurableAgentRuntime.layerWithServices with RunToolAuthorization. The durable runtime captures RunContextPreparation in its Scope. Put hook, transientContext, and compactor in the same service value when using all three.

The engine reloads transient context in every normal or grace Turn and after durable recovery, after canonical context preparation and initial compaction succeed. Failure in that initial phase does not read transient sources. Post-load admission can compact canonical history further to make room for the references. This pass and a same-Turn provider-overflow retry reuse the loaded snapshot. Each provider call still has to fit contextTokenLimit; transient text participates in output-contract, run-status, token budget, and completion-reserve admission. Oversized context fails before provider I/O.

Transient references never enter Thread history, canonical records, compaction coverage, or a compaction summary request. Recovery reads the source again instead of replaying an earlier snapshot. RunContextRequest.source is the current Attempt’s official pre-preparation history; use its stable identities or an application-owned query when retrieval requires canonical durable state.

Define namespaces with an application-owned identity Schema. Constructor inputs retain brands; keys, writes, documents, access values, and index results retain the definition’s name, version, and identity type. Definitions with the same identity fields but different names or versions are not interchangeable.

import {
import MemoryNamespace
MemoryNamespace
} from "@yielded/agent";
import {
type MemoryAccess<Namespace extends MemoryNamespace.Any = { readonly address: string & Brand<"@effect-agent/core/MemoryNamespaceAddress">; }> = Omit<MemoryAccessWire, "namespace"> & {
readonly namespace: Namespace;
}
const MemoryAccess: {
Wire: typeof MemoryAccessWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryAccess<Namespace>) => MemoryAccess<Namespace>;
}
MemoryAccess
} from "@yielded/agent/memory-revalidation";
import {
type MemoryKey<Namespace extends MemoryNamespace.Any = { readonly address: string & Brand<"@effect-agent/core/MemoryNamespaceAddress">; }> = Omit<MemoryKeyWire, "namespace"> & {
readonly namespace: Namespace;
}
const MemoryKey: {
Wire: typeof MemoryKeyWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryKey<Namespace>) => MemoryKey<Namespace>;
}
MemoryKey
,
type MemoryScope = string & Brand<"@effect-agent/core/MemoryScope">
const MemoryScope: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">

Host-defined recall visibility label. A scope identifies access policy; it does not grant it.

MemoryScope
} from "@yielded/agent/memory-store";
import {
import Schema
Schema
} from "effect";
const
const TenantId: Schema.brand<Schema.NonEmptyString, "app/TenantId">
TenantId
=
import Schema
Schema
.
const NonEmptyString: Schema.NonEmptyString

Type-level representation of

NonEmptyString

.

Schema for non-empty strings. Validates that a string has at least one character.

@category ― models

@since ― 3.10.0

@category ― schemas

@since ― 3.10.0

NonEmptyString
.
Pipeable.pipe<Schema.NonEmptyString, Schema.brand<Schema.NonEmptyString, "app/TenantId">>(this: Schema.NonEmptyString, ab: (_: Schema.NonEmptyString) => Schema.brand<Schema.NonEmptyString, "app/TenantId">): Schema.brand<Schema.NonEmptyString, "app/TenantId"> (+21 overloads)
pipe
(
import Schema
Schema
.
function brand<"app/TenantId">(identifier: "app/TenantId"): <S>(schema: S) => Schema.brand<S["Rebuild"], "app/TenantId">

Intersects a schema's output type with Brand.Brand<B> to prevent accidental mixing of structurally identical types.

When to use

Use to make values decoded by an existing schema nominally distinct when the schema already carries the runtime validation you need.

Gotchas

  • identifier must be a single concrete string literal. Widened strings, unions, and open template literal types are rejected.
  • brand only narrows the TypeScript output type. It does not change the schema's runtime AST or add runtime checks.
  • Schema representations and generated schema code omit the brand. Reapply brand after rebuilding or generating a schema when the nominal type is still required.

@see ― fromBrand for applying a Brand constructor's checks along with its branded type

@category ― branding

@since ― 3.10.0

brand
("app/TenantId"));
const
const UserId: Schema.brand<Schema.NonEmptyString, "app/UserId">
UserId
=
import Schema
Schema
.
const NonEmptyString: Schema.NonEmptyString

Type-level representation of

NonEmptyString

.

Schema for non-empty strings. Validates that a string has at least one character.

@category ― models

@since ― 3.10.0

@category ― schemas

@since ― 3.10.0

NonEmptyString
.
Pipeable.pipe<Schema.NonEmptyString, Schema.brand<Schema.NonEmptyString, "app/UserId">>(this: Schema.NonEmptyString, ab: (_: Schema.NonEmptyString) => Schema.brand<Schema.NonEmptyString, "app/UserId">): Schema.brand<Schema.NonEmptyString, "app/UserId"> (+21 overloads)
pipe
(
import Schema
Schema
.
function brand<"app/UserId">(identifier: "app/UserId"): <S>(schema: S) => Schema.brand<S["Rebuild"], "app/UserId">

Intersects a schema's output type with Brand.Brand<B> to prevent accidental mixing of structurally identical types.

When to use

Use to make values decoded by an existing schema nominally distinct when the schema already carries the runtime validation you need.

Gotchas

  • identifier must be a single concrete string literal. Widened strings, unions, and open template literal types are rejected.
  • brand only narrows the TypeScript output type. It does not change the schema's runtime AST or add runtime checks.
  • Schema representations and generated schema code omit the brand. Reapply brand after rebuilding or generating a schema when the nominal type is still required.

@see ― fromBrand for applying a Brand constructor's checks along with its branded type

@category ― branding

@since ― 3.10.0

brand
("app/UserId"));
const
const UserConversations: {
name: "app/user-conversations";
version: 1;
make: (identity: {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}) => MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>;
decode: (input: unknown) => Effect<Readonly<MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect<...>;
}
UserConversations
=
import MemoryNamespace
MemoryNamespace
.
const define: <"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}, {
readonly tenantId: string;
readonly userId: string;
}>(options: {
readonly name: "app/user-conversations";
readonly version: 1;
readonly identity: Schema.Codec<{
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}, {
readonly tenantId: string;
readonly userId: string;
}, never, never>;
}) => {
name: "app/user-conversations";
version: 1;
make: (identity: {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}) => MemoryNamespace.Value<...>;
decode: (input: unknown) => Effect<...>;
restore: (input: unknown) => Effect<...>;
}
define
({
name: "app/user-conversations"
name
: "app/user-conversations",
version: 1
version
: 1,
identity: Schema.Codec<{
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}, {
readonly tenantId: string;
readonly userId: string;
}, never, never>
identity
:
import Schema
Schema
.
function Struct<{
readonly tenantId: Schema.brand<Schema.NonEmptyString, "app/TenantId">;
readonly userId: Schema.brand<Schema.NonEmptyString, "app/UserId">;
}>(fields: {
readonly tenantId: Schema.brand<Schema.NonEmptyString, "app/TenantId">;
readonly userId: Schema.brand<Schema.NonEmptyString, "app/UserId">;
}): Schema.Struct<{
readonly tenantId: Schema.brand<Schema.NonEmptyString, "app/TenantId">;
readonly userId: Schema.brand<Schema.NonEmptyString, "app/UserId">;
}>

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
({
tenantId: Schema.brand<Schema.NonEmptyString, "app/TenantId">
tenantId
:
const TenantId: Schema.brand<Schema.NonEmptyString, "app/TenantId">
TenantId
,
userId: Schema.brand<Schema.NonEmptyString, "app/UserId">
userId
:
const UserId: Schema.brand<Schema.NonEmptyString, "app/UserId">
UserId
}),
});
declare const
const session: {
readonly tenantId: typeof TenantId.Type;
readonly userId: typeof UserId.Type;
}
session
: {
readonly
tenantId: string & Brand<"app/TenantId">
tenantId
: typeof
const TenantId: Schema.brand<Schema.NonEmptyString, "app/TenantId">
TenantId
.
brand<NonEmptyString, "app/TenantId">["Type"]: string & Brand<"app/TenantId">
Type
;
readonly
userId: string & Brand<"app/UserId">
userId
: typeof
const UserId: Schema.brand<Schema.NonEmptyString, "app/UserId">
UserId
.
brand<NonEmptyString, "app/UserId">["Type"]: string & Brand<"app/UserId">
Type
;
};
const
const conversations: MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>
conversations
=
const UserConversations: {
name: "app/user-conversations";
version: 1;
make: (identity: {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}) => MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>;
decode: (input: unknown) => Effect<Readonly<MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect<...>;
}
UserConversations
.
make: (identity: {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}) => MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>
make
(
const session: {
readonly tenantId: typeof TenantId.Type;
readonly userId: typeof UserId.Type;
}
session
);
const
const key: MemoryKey<MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>
key
=
const MemoryKey: {
Wire: typeof MemoryKeyWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryKey<Namespace>) => MemoryKey<Namespace>;
}
MemoryKey
.
make: <MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>(fields: MemoryKey<MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>) => MemoryKey<MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>
make
({
namespace: MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>
namespace
:
const conversations: MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>
conversations
,
id: string
id
: "conversation-42" });
const
const access: MemoryAccess<MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>
access
=
const MemoryAccess: {
Wire: typeof MemoryAccessWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryAccess<Namespace>) => MemoryAccess<Namespace>;
}
MemoryAccess
.
make: <MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>(fields: MemoryAccess<MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>) => MemoryAccess<MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>>
make
({
namespace: MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>
namespace
:
const conversations: MemoryNamespace.Value<"app/user-conversations", 1, {
readonly tenantId: string & Brand<"app/TenantId">;
readonly userId: string & Brand<"app/UserId">;
}>
conversations
,
scope: string & Brand<"@effect-agent/core/MemoryScope">
scope
:
const MemoryScope: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">

Host-defined recall visibility label. A scope identifies access policy; it does not grant it.

MemoryScope
.
BottomWithoutNew<unknown, unknown, unknown, unknown, String, brand<NonEmptyString, "@effect-agent/core/MemoryScope">, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.make(input: string, options?: Schema.MakeOptions): string & Brand<"@effect-agent/core/MemoryScope">

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
("private") });

make takes decoded identity values and throws on invalid construction. decode(unknown) decodes external identity input as an Effect with MemoryNamespaceError and no service requirements. restore(address) validates a stored address against the specific definition, including its identity codec, name, and version. It rejects noncanonical addresses and reports wrong definitions or unsupported address formats separately. Validation does not authenticate a principal. Two tenants still share the same TenantId type. The host must establish session identity and authorization before constructing namespaces or access values. "private" is only an application-defined scope name, not a built-in privacy policy.

Every adapter uses namespace.address, a branded, Schema-validated string. Its format is compact JSON [1, definitionName, definitionVersion, encodedIdentity]. Object keys sort recursively in UTF-16 code-unit order, including numeric-looking keys. Array order is preserved. Strings use JSON escaping without Unicode normalization. JSON number serialization normalizes negative zero to zero. Separator characters cannot join distinct identity fields into the same address.

Identity codecs must be deterministic, synchronous, service-free, and encode to JSON. Branded Structs, records, arrays, and codecs such as Schema.DateFromString are supported. Encoding then decoding normalizes constructor values through the identity codec. Another round trip must leave the encoded identity unchanged. Schema-defined normalization, such as ignored excess Struct fields, deliberately selects the same address. Non-JSON values such as undefined, non-finite numbers, and bigint must be converted by the codec or are rejected.

Names contain 1–256 UTF-16 code units; definition versions are positive safe integers. The full address is at most 4,096 UTF-8 bytes. Encoded identities allow at most 16 nested container levels and 128 entries per container. These limits apply before storage or indexing. Changing a definition version selects a distinct namespace, even for the same identity. It never interprets old memory under a new Schema. Document revisions and SQLite’s storage-format version are separate.

MemoryKey.Wire, MemoryDocument.Wire, MemoryWrite.Wire, and the access/index .Wire Schemas are explicitly heterogeneous transport representations. Their namespaces contain only the canonical address, not a recovered application identity type. Adapter authors use MemoryReader.fromAdapter, MemoryWriter.fromAdapter, and SemanticMemoryIndex.fromAdapter to validate results and restore the caller’s namespace type. For persisted documents outside a port, restore a namespace through its definition, then call MemoryDocument.restore(namespace, input). Never assert a wire value into an application namespace type. Generic types without an explicit namespace parameter describe heterogeneous values; use MemoryKey<typeof conversations> and the equivalent document/write/access/index types for family-specific application APIs.

Namespace identities and addresses can contain sensitive identifiers. They are not retrieval parameters for the model and are not automatically added to recall text, logs, or telemetry. The framework does not supply a registry, tenant membership checks, wildcard search, or a fixed memory taxonomy.

This changes SQLite memory storage to format 2. Format-1 memory data and old string-namespace prepared activity outputs are incompatible and fail decoding. Reset affected development memory and processor data before reusing it. There is no migration or raw-string fallback. Existing Thread history is a separate retention concern.

MemoryReader and MemoryWriter are separate optional capabilities. Use a reader to validate search or cache candidates against the current source. A writer is appropriate only when its adapter can atomically check an expected revision and retain idempotency receipts. A read-only corpus needs no writer. An external memory service can implement these ports without copying its corpus into a framework store.

The host selects a namespace and access scope. A document explicitly lists the scopes allowed to recall it; an empty list grants none. No scope name, shared persona, channel, or DM is enabled by default. Call revalidateMemoryLookup(candidates, access, limits) inside the reader supplied to Memory.recall, immediately before composition. It requires only MemoryReader. The optional third argument accepts maxInputBytes from the recall limits, defaulting to 16 MiB and capped at 64 MiB. Revalidation reads one authoritative source at a time and checks aggregate UTF-8 replacement JSON before retaining it, including duplicate passages. Exceeding this bound fails with MemoryRecallError reason budget before reading later source groups. A single reader result and its schema decoding precede the aggregate retention bound.

Validation binds each returned passage’s private authority to the host-selected namespace, replacing any candidate-supplied authority. Combining independently authorized namespaces therefore preserves their distinct source identities without exposing namespace values in model text. Validation reloads each candidate’s source, excludes missing, withdrawn, or access-revoked documents, and replaces stale text with the current document. Even a same-revision excerpt gets its attribution and metadata from the current source. It survives only if its text occurs there. The usual recall budget can omit a replacement that no longer fits. Source failures stay typed; the consumer must explicitly choose any optional fallback.

import {
import MemoryNamespace
MemoryNamespace
} from "@yielded/agent";
import {
class MemoryContent

Readable authoritative content, independent of any index, model, or fixed memory taxonomy. Recording/extraction time never substitutes for the original source activity time.

MemoryContent
} from "@yielded/agent/memory-reference";
import {
type MemoryKey<Namespace extends MemoryNamespace.Any = { readonly address: string & Brand<"@effect-agent/core/MemoryNamespaceAddress">; }> = Omit<MemoryKeyWire, "namespace"> & {
readonly namespace: Namespace;
}
const MemoryKey: {
Wire: typeof MemoryKeyWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryKey<Namespace>) => MemoryKey<Namespace>;
}
MemoryKey
,
type MemoryScope = string & Brand<"@effect-agent/core/MemoryScope">
const MemoryScope: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">

Host-defined recall visibility label. A scope identifies access policy; it does not grant it.

MemoryScope
,
class MemoryWriter

Optional conditional writer. Implement only where atomic expected-revision checks and durable idempotency receipts can be guaranteed. Apply authorization before calling it. Reconcile an existing operation receipt before evaluating tombstones or expected revisions: identical commands return the original result; changed commands fail MemoryOperationConflict. Original results include content, even for a historical Put replayed after withdrawal; replay does not change the current head. Revalidate a source before using recalled content. A failed acknowledgement can hide a committed change: retain and retry the exact command. Successful withdrawal excludes checks begun afterward; already captured views may finish. Receipts and original Thread history are separate retention concerns.

MemoryWriter
} from "@yielded/agent/memory-store";
import {
import Effect
Effect
,
import Schema
Schema
} from "effect";
const
const TeamMemory: {
name: "app/team-memory";
version: 1;
make: (identity: string) => MemoryNamespace.Value<"app/team-memory", 1, string>;
decode: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
}
TeamMemory
=
import MemoryNamespace
MemoryNamespace
.
const define: <"app/team-memory", 1, string, string>(options: {
readonly name: "app/team-memory";
readonly version: 1;
readonly identity: Schema.Codec<string, string, never, never>;
}) => {
name: "app/team-memory";
version: 1;
make: (identity: string) => MemoryNamespace.Value<"app/team-memory", 1, string>;
decode: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
}
define
({
name: "app/team-memory"
name
: "app/team-memory",
version: 1
version
: 1,
identity: Schema.Codec<string, string, never, never>
identity
:
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
,
});
const
const key: MemoryKey<MemoryNamespace.Value<"app/team-memory", 1, string>>
key
=
const MemoryKey: {
Wire: typeof MemoryKeyWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryKey<Namespace>) => MemoryKey<Namespace>;
}
MemoryKey
.
make: <MemoryNamespace.Value<"app/team-memory", 1, string>>(fields: MemoryKey<MemoryNamespace.Value<"app/team-memory", 1, string>>) => MemoryKey<MemoryNamespace.Value<"app/team-memory", 1, string>>
make
({
namespace: MemoryNamespace.Value<"app/team-memory", 1, string>
namespace
:
const TeamMemory: {
name: "app/team-memory";
version: 1;
make: (identity: string) => MemoryNamespace.Value<"app/team-memory", 1, string>;
decode: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
}
TeamMemory
.
make: (identity: string) => MemoryNamespace.Value<"app/team-memory", 1, string>
make
("team-a"),
id: string
id
: "queue-discussion" });
export const
const correctDiscussion: (content: MemoryContent, expectedRevision: string, operationId: string) => Effect.Effect<MemoryDocument<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryWriteError, MemoryWriter>
correctDiscussion
=
import Effect
Effect
.
const fn: (name: string, options?: SpanOptionsNoTrace) => Effect.fn.Traced (+63 overloads)
fn
("correctDiscussion")(function* (
content: MemoryContent
content
:
class MemoryContent

Readable authoritative content, independent of any index, model, or fixed memory taxonomy. Recording/extraction time never substitutes for the original source activity time.

MemoryContent
,
expectedRevision: string
expectedRevision
: string,
operationId: string
operationId
: string,
) {
const
const writer: {
readonly change: <Namespace extends MemoryNamespace.Any>(write: MemoryWrite<Namespace>) => Effect.Effect<MemoryDocument<Namespace>, MemoryWriteError>;
}
writer
= yield*
class MemoryWriter

Optional conditional writer. Implement only where atomic expected-revision checks and durable idempotency receipts can be guaranteed. Apply authorization before calling it. Reconcile an existing operation receipt before evaluating tombstones or expected revisions: identical commands return the original result; changed commands fail MemoryOperationConflict. Original results include content, even for a historical Put replayed after withdrawal; replay does not change the current head. Revalidate a source before using recalled content. A failed acknowledgement can hide a committed change: retain and retry the exact command. Successful withdrawal excludes checks begun afterward; already captured views may finish. Receipts and original Thread history are separate retention concerns.

MemoryWriter
;
return yield*
const writer: {
readonly change: <Namespace extends MemoryNamespace.Any>(write: MemoryWrite<Namespace>) => Effect.Effect<MemoryDocument<Namespace>, MemoryWriteError>;
}
writer
.
change: <MemoryNamespace.Value<"app/team-memory", 1, string>>(write: MemoryWrite<MemoryNamespace.Value<"app/team-memory", 1, string>>) => Effect.Effect<MemoryDocument<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryWriteError, never>
change
({
_tag: "Put"
_tag
: "Put",
key: MemoryKey<MemoryNamespace.Value<"app/team-memory", 1, string>>
key
,
operationId: string

The same operation ID must always carry exactly the same Schema-encoded command.

operationId
,
expectedRevision: string | null
expectedRevision
,
locator: string
locator
: "chat://engineering/42",
content: MemoryContent
content
,
scopes: readonly (string & Brand<"@effect-agent/core/MemoryScope">)[]
scopes
: [
const MemoryScope: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">

Host-defined recall visibility label. A scope identifies access policy; it does not grant it.

MemoryScope
.
BottomWithoutNew<unknown, unknown, unknown, unknown, String, brand<NonEmptyString, "@effect-agent/core/MemoryScope">, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.make(input: string, options?: Schema.MakeOptions): string & Brand<"@effect-agent/core/MemoryScope">

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
("participating-channels")],
});
});
export const
const withdrawDiscussion: (expectedRevision: string, operationId: string) => Effect.Effect<MemoryDocument<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryWriteError, MemoryWriter>
withdrawDiscussion
=
import Effect
Effect
.
const fn: (name: string, options?: SpanOptionsNoTrace) => Effect.fn.Traced (+63 overloads)
fn
("withdrawDiscussion")(function* (
expectedRevision: string
expectedRevision
: string,
operationId: string
operationId
: string,
) {
const
const writer: {
readonly change: <Namespace extends MemoryNamespace.Any>(write: MemoryWrite<Namespace>) => Effect.Effect<MemoryDocument<Namespace>, MemoryWriteError>;
}
writer
= yield*
class MemoryWriter

Optional conditional writer. Implement only where atomic expected-revision checks and durable idempotency receipts can be guaranteed. Apply authorization before calling it. Reconcile an existing operation receipt before evaluating tombstones or expected revisions: identical commands return the original result; changed commands fail MemoryOperationConflict. Original results include content, even for a historical Put replayed after withdrawal; replay does not change the current head. Revalidate a source before using recalled content. A failed acknowledgement can hide a committed change: retain and retry the exact command. Successful withdrawal excludes checks begun afterward; already captured views may finish. Receipts and original Thread history are separate retention concerns.

MemoryWriter
;
return yield*
const writer: {
readonly change: <Namespace extends MemoryNamespace.Any>(write: MemoryWrite<Namespace>) => Effect.Effect<MemoryDocument<Namespace>, MemoryWriteError>;
}
writer
.
change: <MemoryNamespace.Value<"app/team-memory", 1, string>>(write: MemoryWrite<MemoryNamespace.Value<"app/team-memory", 1, string>>) => Effect.Effect<MemoryDocument<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryWriteError, never>
change
({
_tag: "Withdraw"
_tag
: "Withdraw",
key: MemoryKey<MemoryNamespace.Value<"app/team-memory", 1, string>>
key
,
operationId: string

The same operation ID must always carry exactly the same Schema-encoded command.

operationId
,
expectedRevision: string
expectedRevision
,
reason: string
reason
: "Withdrawn by the source owner",
});
});

Create a source with Put and expectedRevision: null. Later writes use the current revision; a competing edit returns MemoryConflict without discarding the winning edit. Every replacement records its predecessor reference. modifiedAt tracks the update, while the caller’s original activity, recording, and extraction times remain separate. Applications decide how to correct attribution, resolve conflicting claims, and age discussions or commitments. Timestamps alone never choose a winning claim.

Retry an uncertain write with its original operationId and exactly the same Schema-encoded command. A successful replay returns the original receipt’s document and does not undo later edits or withdrawal. Reusing an operation ID for different content returns MemoryOperationConflict. Revalidate the returned document before recall because a receipt can describe an older revision.

Withdrawal is terminal for that source ID within its namespace. A committed withdrawal excludes the source from validation checks begun afterward. The same rule applies after access revocation. An already captured view, including a same-Turn provider retry, may finish. This guarantee needs an authoritative reader; an eventually consistent service must refuse a view it cannot validate. Do not run the SQLite reader inside a caller-owned stale snapshot transaction.

Withdrawal governs future recall. Original Thread records, past model outputs, idempotency receipts, and backups have separate retention policies. Sensitive-information screening is best effort and does not authorize sharing or guarantee privacy.

For a local persistent source, install the optional SQLite adapter:

import {
const memoryStoreLayer: Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, SqlClient>

SQLite memory reader, single writer and batch writer with the production no-op failpoint.

memoryStoreLayer
} from "@yielded/agent/sql-memory-store";
import {
import SqliteClient
SqliteClient
} from "@effect/sql-sqlite-node";
import {
import Layer
Layer
} from "effect";
export const
const MemoryLive: Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, never>
MemoryLive
=
const memoryStoreLayer: Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, SqlClient>

SQLite memory reader, single writer and batch writer with the production no-op failpoint.

memoryStoreLayer
.
Pipeable.pipe<Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, SqlClient>, Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, never>>(this: Layer.Layer<...>, ab: (_: Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, SqlClient>) => Layer.Layer<...>): Layer.Layer<...> (+21 overloads)
pipe
(
import Layer
Layer
.
const provide: <never, never, SqlClient | SqliteClient.SqliteClient>(that: Layer.Layer<SqlClient | SqliteClient.SqliteClient, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, E2, Exclude<RIn2, SqlClient | SqliteClient.SqliteClient>> (+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 SqliteClient
SqliteClient
.
const layer: (config: SqliteClient.SqliteClientConfig) => Layer.Layer<SqliteClient.SqliteClient | SqlClient>

Builds a layer from a node SQLite client configuration, providing both SqliteClient and the generic SqlClient service.

@category ― layers

@since ― 4.0.0

layer
({
SqliteClientConfig.filename: string
filename
: "memory.sqlite" })),
);

memoryStoreLayer provides both ports and creates only its own memory tables. It does not initialize Thread history or a Submission Ledger. memoryReaderLayer checks an existing schema without creating tables or starting a write transaction, and supports SqliteClient.layer({ filename, readonly: true }). It fails with MemoryStorageError when the schema is absent or incompatible; initialize it through memoryStoreLayer in the writer process first. The connection belongs to its Layer’s Scope. The adapter rejects a change before writing when its canonical command, document, or receipt-result JSON exceeds 16,777,216 JavaScript string code units. memoryStoreLayerWithFailpoints accepts a MemoryMutationFailpoint service for transaction and lost-acknowledgement tests. Replayed receipts must match their original command’s predecessor, result kind, content, locator, scopes, and withdrawal reason; mismatches fail as corrupt data. Recall and validation helpers add named Effect spans without source text or metadata annotations. For shared Worker memory, use the Cloudflare memory owner. It validates an entire candidate batch locally and returns one attributed response over one RPC. RecalledMemory.outcomes reports source availability and selected, deduplicated, and omitted counts; the host decides which diagnostics to retain and who can inspect them.

Remembering separates durable admission from extraction and memory updates. The foreground submits an identity-bound intent and receives a queued acknowledgement after persistence succeeds. Queued means accepted for processing. It does not mean that a fact was accepted, saved, or made available to another Thread. Admission adds bounded persistence work and can fail with a typed error.

Use Remembering.admit(store, intent) from @yielded/agent/remembering for admission and Remembering.make({ proposal, loadSource, extract, merge, cleanup }).advance(...) for a finite worker pass. The RememberingStore module defines the portable Schemas and injectable port. Use native Effect AI extraction and source-aware profile callbacks. Provide RememberingStore.MutationFailpoint.layer in production and replace it for fault tests.

Automatic remembering follows the host’s committed source activity and outbox. It needs no memory Tool or extra model turn. The outbox retains activity when admission is unavailable or full; that backlog does not turn a completed chat response into a failure. Explicit remember actions report admission failure instead of claiming success.

The host runs a finite processing pass in a separate Scope with its own concurrency and model budgets. It owns discovery, source order, job quotas, retry deadlines, parking, and wake repair. Do not await processing after AgentRuntime.run: an awaited callback still delays the response. Do not hold a producer lock, database transaction, or all foreground provider permits while extracting, checking evidence, reading profiles, writing, retrying conflicts, or cleaning up.

The protocol saves two different values:

  1. An extracted proposal, encoded with the application’s Schema and bound to the admitted source.
  2. The exact prepared MemoryWrite, including its operation ID, expected revision, scopes, content, and content timestamps, before dispatch to MemoryWriter.

Unknown write outcomes retry that saved command unchanged. Only a definite MemoryConflict allows a new operation ID and rebase against the latest target. Rebase retains the saved proposal and does not repeat extraction. The application supplies source-aware merging so concurrent source contributions and human corrections survive. An operation-ID/content mismatch remains MemoryOperationConflict; it is not permission to create another command.

loadSource checks the current authorized source after extraction and before saving a new command. It can return an authoritative invalidation event, which the worker durably admits. A saved command is already an uncertain write and must reconcile even if the source is now unavailable. Source changes after preparation therefore rely on durable suppression and current-source recall checks. Extraction may return null when there is no accepted fact; that finishes without reading a target.

The application also supplies canonical source loading, fact acceptance, authorization, and conditional cleanup. Evidence must identify a known source revision and a literal quote or range from that source. The model cannot select a tenant, target, or authority. A valid quote proves provenance, not the truth of an extracted claim.

Source edits, deletion, revocation, and Forget require durable suppression and cleanup admission before acknowledging the source event. Suppression does not discard an uncertain command: reconciliation must still establish its outcome and remove obsolete source-owned contributions. Current-source recall checks exclude invalid evidence while cleanup is pending. Human corrections have independent provenance and survive source cleanup. Disable new extraction separately from required reconciliation and cleanup.

Active job removal must retain bounded source-to-target references, so later invalidation finds completed contributions without a prior read. Retain those references, suppression, and admission and write receipts throughout the host’s supported replay, backfill, and restore window. Reject new work at capacity without evicting existing obligations. Hosts define source-position authority. Sequence numbers are ordered only within the same opaque authority generation. Different generations are incomparable, and revision strings are never ordered. The host fences old workers and performs any restore or authority cutover explicitly. An old database snapshot cannot prove that no later Forget or revocation occurred. Verify its lineage against the current source authority outside that restored snapshot and reject incompatible state before processing or recall. The protocol supplies no migration or automatic authority cutover.

Recall remains the existing transient read path. Load current permitted memory and run the application’s grouped canonical-source checks without draining jobs, waiting for readiness, or refreshing an index. Track first token, completed response, admission duration, and source-to-authorized-recall delay separately. Healthy background progress and cross-Thread freshness depend on the host’s scheduling and source availability.

Callbacks retain their typed errors and Effect service requirements. Defects and interruption remain distinct from expected failure; a processing deadline interrupts cooperative work and runs its finalizers. The helpers use named Effect spans without attaching source text, proposals, profiles, or private namespace identities. Hosts choose retry policy and operational metrics.

processCommittedActivity is an optional, finite pass over one application-selected Thread. The host chooses the processor ID and version, eligible records, extractor, destination, sharing scope, invocation schedule, and Thread discovery. No background worker starts when the package is imported. A read-only corpus or direct Markdown recall needs none of these services.

The pass claims its own processor lease and receives any pending output atomically with that claim. It captures ThreadStore.inspectTail once and reads bounded, contiguous pages through that prefix. Records appended afterward belong to a later pass. Pending work beyond the captured tail fails as noncontiguous, including when progress and Thread storage were restored to different points. PersistentHistory exposes the batch from a successful Run together; durable Runs expose incremental committed records. A record’s presence is evidence of its commit, not evidence that the whole Run succeeded. Eligibility rules must account for that distinction.

For each record the processor saves a Schema-encoded extraction, applies that saved output, and then advances its separate cursor. The work ID depends on processor, version, Thread, and sequence, never the worker or clock. Advancing clears pending output in the same transaction; there is no separate pending-work read per record. A saved output wins over re-extraction after restart. Its canonical record digest excludes the adapter’s opaque observation cursor. Another processor version has independent progress; changing a version is an application decision about reprocessing and destination identities.

The destination must reconcile the work ID durably before returning. If application and progress storage are separate, application can repeat after a lost acknowledgment. Keep its receipts for as long as pending output can return, including supported backup/restore windows. Conditional writes must prevent delayed work from overwriting later corrections or withdrawals. The SQLite memory writer supplies those properties; an ordinary non-idempotent external effect does not.

This example opts a structured Dan–Chad discussion into a scope the host also grants Tim. Its extraction policy accepts only original user statements. An assistant repeating the statement does not become another witness. Applications that extract assistant references should retain the original originId instead of assigning independent evidence identity.

import {
import MemoryNamespace
MemoryNamespace
} from "@yielded/agent";
import {
class MemoryContent

Readable authoritative content, independent of any index, model, or fixed memory taxonomy. Recording/extraction time never substitutes for the original source activity time.

MemoryContent
} from "@yielded/agent/memory-reference";
import {
type MemoryKey<Namespace extends MemoryNamespace.Any = { readonly address: string & Brand<"@effect-agent/core/MemoryNamespaceAddress">; }> = Omit<MemoryKeyWire, "namespace"> & {
readonly namespace: Namespace;
}
const MemoryKey: {
Wire: typeof MemoryKeyWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryKey<Namespace>) => MemoryKey<Namespace>;
}
MemoryKey
,
type MemoryScope = string & Brand<"@effect-agent/core/MemoryScope">
const MemoryScope: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">

Host-defined recall visibility label. A scope identifies access policy; it does not grant it.

MemoryScope
,
type MemoryWrite<Namespace extends MemoryNamespace.Any = { readonly address: string & Brand<"@effect-agent/core/MemoryNamespaceAddress">; }> = (Omit<{
readonly _tag: "Put";
readonly expectedRevision: string | null;
readonly locator: string;
readonly content: MemoryContent;
readonly scopes: readonly (string & Brand<"@effect-agent/core/MemoryScope">)[];
readonly key: MemoryKeyWire;
readonly operationId: string;
}, "key"> | Omit<{
readonly _tag: "Withdraw";
readonly expectedRevision: string;
readonly reason: string;
readonly key: MemoryKeyWire;
readonly operationId: string;
}, "key">) & {
readonly key: MemoryKey<Namespace>;
}
const MemoryWrite: {
Wire: Schema.Union<readonly [Schema.TaggedStruct<"Put", {
readonly expectedRevision: Schema.NullOr<Schema.NonEmptyString>;
readonly locator: Schema.NonEmptyString;
readonly content: typeof MemoryContent;
readonly scopes: Schema.$Array<Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">>;
readonly key: typeof MemoryKeyWire;
readonly operationId: Schema.NonEmptyString;
}>, Schema.TaggedStruct<"Withdraw", {
readonly expectedRevision: Schema.NonEmptyString;
readonly reason: Schema.String;
readonly key: typeof MemoryKeyWire;
readonly operationId: Schema.NonEmptyString;
}>]>;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryWrite<Namespace>) => MemoryWrite<Namespace>;
}
MemoryWrite
,
class MemoryWriter

Optional conditional writer. Implement only where atomic expected-revision checks and durable idempotency receipts can be guaranteed. Apply authorization before calling it. Reconcile an existing operation receipt before evaluating tombstones or expected revisions: identical commands return the original result; changed commands fail MemoryOperationConflict. Original results include content, even for a historical Put replayed after withdrawal; replay does not change the current head. Revalidate a source before using recalled content. A failed acknowledgement can hide a committed change: retain and retry the exact command. Successful withdrawal excludes checks begun afterward; already captured views may finish. Receipts and original Thread history are separate retention concerns.

MemoryWriter
} from "@yielded/agent/memory-store";
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 {
class ActivityPassLimits
ActivityPassLimits
,
const processCommittedActivity: <E = never, R = never, EApply = never, RApply = never>(processor: CommittedActivityProcessor<E, R, EApply, RApply>) => Effect.Effect<ActivityPassResult, E | EApply | ActivityStoreFailure | ActivityProcessingError | DigestError | ThreadStoreError | ThreadNotMaterialized, ActivityProcessorStore | ThreadStore | Crypto | Exclude<R, Scope> | Exclude<...>>

Process one bounded committed prefix of one application-selected Thread. Nothing runs until invoked, and no daemon, global Thread directory, admission ledger, or engine checkpoint is involved. PersistentHistory makes a successful Run's batch visible together; durable Runs expose incremental committed records. The application decides what each record means.

A durable pending output wins over a new extraction after restart. Apply finishes before progress advances, so separate output stores require durable idempotency. This does not promise exactly-once external side effects. Each callback owns a Scope; the finite pass has a timeout and a longer lease. Claim release has a separate 500ms deadline; failure leaves the bounded lease to expire.

processCommittedActivity
} from "@yielded/agent/committed-activity";
import {
class ActivityProcessorKey

Application-owned processor identity. Changing the version starts independent progress.

ActivityProcessorKey
, type
class PreparedActivity

Durable extraction output, pinned before any external application of that output.

PreparedActivity
} from "@yielded/agent/activity-store";
import { type
class CanonicalRecordEnvelope

A committed record with its Thread ordering and opaque resumable observation cursor.

CanonicalRecordEnvelope
} from "@yielded/agent/records";
import {
import Clock
Clock
,
import DateTime
DateTime
,
import Effect
Effect
,
import Schema
Schema
} from "effect";
// The application owns this message format and which Threads use it.
const
const Statement: Schema.Struct<{
readonly speaker: Schema.NonEmptyString;
readonly observer: Schema.NonEmptyString;
readonly text: Schema.NonEmptyString;
readonly activityAt: Schema.NullOr<Schema.Finite>;
readonly interpretation: Schema.NonEmptyString;
}>
Statement
=
import Schema
Schema
.
function Struct<{
readonly speaker: Schema.NonEmptyString;
readonly observer: Schema.NonEmptyString;
readonly text: Schema.NonEmptyString;
readonly activityAt: Schema.NullOr<Schema.Finite>;
readonly interpretation: Schema.NonEmptyString;
}>(fields: {
readonly speaker: Schema.NonEmptyString;
readonly observer: Schema.NonEmptyString;
readonly text: Schema.NonEmptyString;
readonly activityAt: Schema.NullOr<Schema.Finite>;
readonly interpretation: Schema.NonEmptyString;
}): Schema.Struct<...>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types. Declared fields may be inherited and are copied to own properties in the output. The __proto__ field is accepted only when it is an own property. Parsing does not guarantee that output keys retain their input order.

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
({
speaker: Schema.NonEmptyString
speaker
:
import Schema
Schema
.
const NonEmptyString: Schema.NonEmptyString

Type-level representation of

NonEmptyString

.

Schema for non-empty strings. Validates that a string has at least one character.

@category ― models

@since ― 3.10.0

@category ― schemas

@since ― 3.10.0

NonEmptyString
,
observer: Schema.NonEmptyString
observer
:
import Schema
Schema
.
const NonEmptyString: Schema.NonEmptyString

Type-level representation of

NonEmptyString

.

Schema for non-empty strings. Validates that a string has at least one character.

@category ― models

@since ― 3.10.0

@category ― schemas

@since ― 3.10.0

NonEmptyString
,
text: Schema.NonEmptyString
text
:
import Schema
Schema
.
const NonEmptyString: Schema.NonEmptyString

Type-level representation of

NonEmptyString

.

Schema for non-empty strings. Validates that a string has at least one character.

@category ― models

@since ― 3.10.0

@category ― schemas

@since ― 3.10.0

NonEmptyString
,
activityAt: Schema.NullOr<Schema.Finite>
activityAt
:
import Schema
Schema
.
const NullOr: NullOrLambda
<Schema.Finite>(self: Schema.Finite) => Schema.NullOr<Schema.Finite>

Type-level representation returned by

NullOr

.

Creates a union schema of S | null.

@category ― models

@since ― 3.10.0

@category ― constructors

@since ― 3.10.0

NullOr
(
import Schema
Schema
.
const Finite: Schema.Finite

Type-level representation of

Finite

.

Schema for finite numbers, rejecting NaN, Infinity, and -Infinity.

@category ― models

@since ― 3.10.0

@category ― schemas

@since ― 3.10.0

Finite
),
interpretation: Schema.NonEmptyString
interpretation
:
import Schema
Schema
.
const NonEmptyString: Schema.NonEmptyString

Type-level representation of

NonEmptyString

.

Schema for non-empty strings. Validates that a string has at least one character.

@category ― models

@since ― 3.10.0

@category ― schemas

@since ― 3.10.0

NonEmptyString
,
});
const
const extract: (entry: CanonicalRecordEnvelope) => Effect.Effect<{
readonly text: string;
readonly attributions: readonly {
readonly originId: string;
readonly speaker: string;
readonly observers: readonly string[];
readonly locator: string;
readonly activityAt: number | null;
readonly interpretation: string;
}[];
readonly metadata: {
readonly [x: string]: Schema.Json;
};
readonly recordedAt: number;
readonly extractedAt?: number | undefined;
} | null, Issue | Schema.SchemaError, never>
extract
=
import Effect
Effect
.
const fn: (name: string, options?: SpanOptionsNoTrace) => Effect.fn.Traced (+63 overloads)
fn
("extractDiscussion")(function* (
entry: CanonicalRecordEnvelope
entry
:
class CanonicalRecordEnvelope

A committed record with its Thread ordering and opaque resumable observation cursor.

CanonicalRecordEnvelope
) {
if (
entry: CanonicalRecordEnvelope
entry
.
record: RecordEnvelope
record
.
payload: UserInputRecorded | ModelResponseRecorded | CompactionCreated | ThreadCreated | RunStartedRecord | RunDurationExhausted | RunPolicyUsageReserved | ModelCompleted | ToolCallSettled | ToolCallUnknown | ToolCallResolved | ToolStepSettled | ToolApprovalRequested | ToolApprovalDecided | ... 23 more ... | RunContinuation
payload
.
_tag: "RunContinuation" | "UserInputRecorded" | "ModelCompleted" | "ModelResponseRecorded" | "ToolCallSettled" | "CompactionCreated" | "RunCompleted" | "RunFailed" | "SubmissionSettled" | "ThreadCreated" | "RunStarted" | "RunDurationExhausted" | "RunPolicyUsageReserved" | "ToolCallUnknown" | "ToolCallResolved" | "ToolStepSettled" | "ToolApprovalRequested" | "ToolApprovalDecided" | "ModelResponseInterrupted" | "ModelCallAborted" | "AbortRequested" | "SubagentRequested" | "SubagentStarted" | "SubagentJoined" | "SubagentLineageRecorded" | ... 12 more ... | "RunContextRecorded"
_tag
!== "UserInputRecorded") return null;
const
const statement: {
readonly speaker: string;
readonly observer: string;
readonly text: string;
readonly activityAt: number | null;
readonly interpretation: string;
}
statement
= yield*
import Schema
Schema
.
function decodeUnknownEffect<Schema.Struct<{
readonly speaker: Schema.NonEmptyString;
readonly observer: Schema.NonEmptyString;
readonly text: Schema.NonEmptyString;
readonly activityAt: Schema.NullOr<Schema.Finite>;
readonly interpretation: Schema.NonEmptyString;
}>>(schema: Schema.Struct<{
readonly speaker: Schema.NonEmptyString;
readonly observer: Schema.NonEmptyString;
readonly text: Schema.NonEmptyString;
readonly activityAt: Schema.NullOr<Schema.Finite>;
readonly interpretation: Schema.NonEmptyString;
}>, options?: ParseOptions): (input: unknown, options?: ParseOptions) => Effect.Effect<...>

Decodes an unknown input against a schema, returning an Effect that succeeds with the decoded value or fails with a

SchemaError

.

When to use

Use when you need to decode unknown input in an Effect whose failure channel is SchemaError.

Details

Prefer

decodeEffect

when the input is already typed as the schema's Encoded type. Options may be provided either when creating the decoder or when applying it; application options override creation options.

@see ― SchemaParser.decodeUnknownEffect for the adapter that fails with SchemaIssue.Issue directly

@category ― decoding

@since ― 4.0.0

decodeUnknownEffect
(
const Statement: Schema.Struct<{
readonly speaker: Schema.NonEmptyString;
readonly observer: Schema.NonEmptyString;
readonly text: Schema.NonEmptyString;
readonly activityAt: Schema.NullOr<Schema.Finite>;
readonly interpretation: Schema.NonEmptyString;
}>
Statement
)(
entry: CanonicalRecordEnvelope
entry
.
record: RecordEnvelope
record
.
payload: UserInputRecorded
payload
.
input: Schema.Json
input
);
const
const locator: string
locator
= `thread://${
entry: CanonicalRecordEnvelope
entry
.
threadId: string & Brand<"@effect-agent/core/ThreadId">
threadId
}/records/${
entry: CanonicalRecordEnvelope
entry
.
record: RecordEnvelope
record
.
recordId: string & Brand<"@effect-agent/thread/RecordId">
recordId
}`;
const
const content: MemoryContent
content
= yield*
class MemoryContent

Readable authoritative content, independent of any index, model, or fixed memory taxonomy. Recording/extraction time never substitutes for the original source activity time.

MemoryContent
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<MemoryContent, { readonly text: string; readonly attributions: readonly { readonly originId: string; readonly speaker: string; readonly observers: readonly string[]; readonly locator: string; readonly activityAt: number | null; readonly interpretation: string; }[]; readonly metadata: { ...; }; readonly recordedAt: number; readonly extractedAt?: number | undefined; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.makeEffect(input: {
readonly text: string;
readonly attributions: readonly MemoryAttribution[];
readonly metadata: {
readonly [x: string]: unknown;
};
readonly recordedAt: number;
readonly extractedAt?: number | undefined;
}, options?: Schema.MakeOptions): Effect.Effect<MemoryContent, Issue, never>

Constructs a value from the make input representation, returning validation failures in the Effect error channel.

When to use

Use when construction must compose with other Effect operations or when the result supplies a nested constructor default. Otherwise, use

BottomWithoutNew.make

when invalid construction should throw.

Details

SchemaParser uses SchemaIssue.Issue as its canonical structured failure, and this method exposes the same representation directly. Keeping the issue unwrapped lets an enclosing schema attach its own path when the effect is used with

withConstructorDefault

. It also avoids allocating a SchemaError and keeps its formatting path out of the resulting bundle.

Gotchas

Unlike the decoding helpers in Schema, validation failures are not wrapped in SchemaError. Map the issue to SchemaError or to a domain-specific error when a wrapped error is needed at the application boundary.

@see ― BottomWithoutNew.make — construct synchronously when validation failure should throw

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

makeEffect
({
text: string
text
:
const statement: {
readonly speaker: string;
readonly observer: string;
readonly text: string;
readonly activityAt: number | null;
readonly interpretation: string;
}
statement
.
text: string
text
,
attributions: readonly MemoryAttribution[]
attributions
: [
{
originId: string
originId
: `${
entry: CanonicalRecordEnvelope
entry
.
threadId: string & Brand<"@effect-agent/core/ThreadId">
threadId
}:${
entry: CanonicalRecordEnvelope
entry
.
record: RecordEnvelope
record
.
recordId: string & Brand<"@effect-agent/thread/RecordId">
recordId
}`,
speaker: string
speaker
:
const statement: {
readonly speaker: string;
readonly observer: string;
readonly text: string;
readonly activityAt: number | null;
readonly interpretation: string;
}
statement
.
speaker: string
speaker
,
observers: readonly string[]
observers
: [
const statement: {
readonly speaker: string;
readonly observer: string;
readonly text: string;
readonly activityAt: number | null;
readonly interpretation: string;
}
statement
.
observer: string
observer
],
locator: string
locator
,
activityAt: number | null
activityAt
:
const statement: {
readonly speaker: string;
readonly observer: string;
readonly text: string;
readonly activityAt: number | null;
readonly interpretation: string;
}
statement
.
activityAt: number | null
activityAt
,
interpretation: string
interpretation
:
const statement: {
readonly speaker: string;
readonly observer: string;
readonly text: string;
readonly activityAt: number | null;
readonly interpretation: string;
}
statement
.
interpretation: string
interpretation
,
},
],
metadata: {
readonly [x: string]: unknown;
}
metadata
: {
threadId: string & Brand<"@effect-agent/core/ThreadId">
threadId
:
entry: CanonicalRecordEnvelope
entry
.
threadId: string & Brand<"@effect-agent/core/ThreadId">
threadId
,
recordId: string & Brand<"@effect-agent/thread/RecordId">
recordId
:
entry: CanonicalRecordEnvelope
entry
.
record: RecordEnvelope
record
.
recordId: string & Brand<"@effect-agent/thread/RecordId">
recordId
,
recordSchemaVersion: 1
recordSchemaVersion
:
entry: CanonicalRecordEnvelope
entry
.
record: RecordEnvelope
record
.
schemaVersion: 1
schemaVersion
,
sequence: number & Brand<"@effect-agent/thread/CanonicalSequence">
sequence
:
entry: CanonicalRecordEnvelope
entry
.
sequence: number & Brand<"@effect-agent/thread/CanonicalSequence">
sequence
,
},
recordedAt: number
recordedAt
:
import DateTime
DateTime
.
const toEpochMillis: (self: DateTime.DateTime) => number

Gets the milliseconds since the Unix epoch of a DateTime.

Details

This returns the UTC timestamp regardless of any time zone information.

Example (Reading epoch milliseconds)

import { DateTime } from "effect"
const dt = DateTime.makeUnsafe("2024-01-01T00:00:00Z")
DateTime.toEpochMillis(dt) // => 1704067200000

@category ― converting

@since ― 3.6.0

toEpochMillis
(
entry: CanonicalRecordEnvelope
entry
.
record: RecordEnvelope
record
.
createdAt: DateTime.Utc
createdAt
),
extractedAt?: number | undefined
extractedAt
: yield*
import Clock
Clock
.
const currentTimeMillis: Effect.Effect<number, never, never>

Returns an Effect that succeeds with the current Unix time in milliseconds.

When to use

Use to create wall-clock timestamps from the active Clock service with millisecond precision.

Gotchas

The value can move backward or forward when the system wall clock is corrected, so it is not suitable for measuring elapsed time.

Example (Reading milliseconds)

import { Clock, Effect } from "effect"
const testClock: Clock.Clock = {
currentTimeMillisUnsafe: () => 1_000,
currentTimeMillis: Effect.succeed(1_000),
monotonicTimeNanosUnsafe: () => 1_000_000_000n,
monotonicTimeNanos: Effect.succeed(1_000_000_000n),
currentTimeNanosUnsafe: () => 1_000_000_000n,
currentTimeNanos: Effect.succeed(1_000_000_000n),
sleep: () => Effect.void
}
await Effect.runPromise(Effect.provideService(Clock.currentTimeMillis, Clock.Clock, testClock)) // => 1_000

@see ― currentTimeNanos for nanosecond precision

@see ― monotonicTimeNanos for measuring elapsed time

@see ― clockWith for accessing the full Clock service

@category ― accessors

@since ― 2.0.0

currentTimeMillis
,
});
return yield*
import Schema
Schema
.
const encodeEffect: <typeof MemoryContent>(schema: typeof MemoryContent, options?: ParseOptions) => (input: MemoryContent, options?: ParseOptions) => Effect.Effect<{
readonly text: string;
readonly attributions: readonly {
readonly originId: string;
readonly speaker: string;
readonly observers: readonly string[];
readonly locator: string;
readonly activityAt: number | null;
readonly interpretation: string;
}[];
readonly metadata: {
readonly [x: string]: Schema.Json;
};
readonly recordedAt: number;
readonly extractedAt?: number | undefined;
}, Schema.SchemaError, never>

Encodes a typed input (the schema's Type) against a schema, returning an Effect that succeeds with the encoded value or fails with a

SchemaError

.

When to use

Use when you need to encode input already typed as the schema's Type in an Effect whose failure channel is SchemaError.

Details

For unknown input use

encodeUnknownEffect

. Options may be provided either when creating the encoder or when applying it; application options override creation options.

@see ― SchemaParser.encodeEffect for the adapter that fails with SchemaIssue.Issue directly

@category ― encoding

@since ― 4.0.0

encodeEffect
(
class MemoryContent

Readable authoritative content, independent of any index, model, or fixed memory taxonomy. Recording/extraction time never substitutes for the original source activity time.

MemoryContent
)(
const content: MemoryContent
content
);
});
const
const apply: (work: PreparedActivity) => Effect.Effect<void, MemoryWriteError | Schema.SchemaError, MemoryWriter>
apply
=
import Effect
Effect
.
const fn: (name: string, options?: SpanOptionsNoTrace) => Effect.fn.Traced (+63 overloads)
fn
("applyDiscussion")(function* (
work: PreparedActivity
work
:
class PreparedActivity

Durable extraction output, pinned before any external application of that output.

PreparedActivity
) {
const
const content: MemoryContent | null
content
= yield*
import Schema
Schema
.
function decodeUnknownEffect<Schema.NullOr<typeof MemoryContent>>(schema: Schema.NullOr<typeof MemoryContent>, options?: ParseOptions): (input: unknown, options?: ParseOptions) => Effect.Effect<MemoryContent | null, Schema.SchemaError, never>

Decodes an unknown input against a schema, returning an Effect that succeeds with the decoded value or fails with a

SchemaError

.

When to use

Use when you need to decode unknown input in an Effect whose failure channel is SchemaError.

Details

Prefer

decodeEffect

when the input is already typed as the schema's Encoded type. Options may be provided either when creating the decoder or when applying it; application options override creation options.

@see ― SchemaParser.decodeUnknownEffect for the adapter that fails with SchemaIssue.Issue directly

@category ― decoding

@since ― 4.0.0

decodeUnknownEffect
(
import Schema
Schema
.
const NullOr: NullOrLambda
<typeof MemoryContent>(self: typeof MemoryContent) => Schema.NullOr<typeof MemoryContent>

Type-level representation returned by

NullOr

.

Creates a union schema of S | null.

@category ― models

@since ― 3.10.0

@category ― constructors

@since ― 3.10.0

NullOr
(
class MemoryContent

Readable authoritative content, independent of any index, model, or fixed memory taxonomy. Recording/extraction time never substitutes for the original source activity time.

MemoryContent
))(
work: PreparedActivity
work
.
output: Schema.Json
output
);
if (
const content: MemoryContent | null
content
=== null) return;
const
const writer: {
readonly change: <Namespace extends MemoryNamespace.Any>(write: MemoryWrite<Namespace>) => Effect.Effect<MemoryDocument<Namespace>, MemoryWriteError>;
}
writer
= yield*
class MemoryWriter

Optional conditional writer. Implement only where atomic expected-revision checks and durable idempotency receipts can be guaranteed. Apply authorization before calling it. Reconcile an existing operation receipt before evaluating tombstones or expected revisions: identical commands return the original result; changed commands fail MemoryOperationConflict. Original results include content, even for a historical Put replayed after withdrawal; replay does not change the current head. Revalidate a source before using recalled content. A failed acknowledgement can hide a committed change: retain and retry the exact command. Successful withdrawal excludes checks begun afterward; already captured views may finish. Receipts and original Thread history are separate retention concerns.

MemoryWriter
;
const
const namespace: MemoryNamespace.Value<"app/discussions", 1, string>
namespace
=
import MemoryNamespace
MemoryNamespace
.
const define: <"app/discussions", 1, string, string>(options: {
readonly name: "app/discussions";
readonly version: 1;
readonly identity: Schema.Codec<string, string, never, never>;
}) => {
name: "app/discussions";
version: 1;
make: (identity: string) => MemoryNamespace.Value<"app/discussions", 1, string>;
decode: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/discussions", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/discussions", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
}
define
({
name: "app/discussions"
name
: "app/discussions",
version: 1
version
: 1,
identity: Schema.Codec<string, string, never, never>
identity
:
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
,
}).
make: (identity: string) => MemoryNamespace.Value<"app/discussions", 1, string>
make
("dan");
const
const command: MemoryWrite<MemoryNamespace.Value<"app/discussions", 1, string>>
command
=
const MemoryWrite: {
Wire: Schema.Union<readonly [Schema.TaggedStruct<"Put", {
readonly expectedRevision: Schema.NullOr<Schema.NonEmptyString>;
readonly locator: Schema.NonEmptyString;
readonly content: typeof MemoryContent;
readonly scopes: Schema.$Array<Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">>;
readonly key: typeof MemoryKeyWire;
readonly operationId: Schema.NonEmptyString;
}>, Schema.TaggedStruct<"Withdraw", {
readonly expectedRevision: Schema.NonEmptyString;
readonly reason: Schema.String;
readonly key: typeof MemoryKeyWire;
readonly operationId: Schema.NonEmptyString;
}>]>;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryWrite<Namespace>) => MemoryWrite<Namespace>;
}
MemoryWrite
.
make: <MemoryNamespace.Value<"app/discussions", 1, string>>(fields: MemoryWrite<MemoryNamespace.Value<"app/discussions", 1, string>>) => MemoryWrite<MemoryNamespace.Value<"app/discussions", 1, string>>
make
({
_tag: "Put"
_tag
: "Put",
key: MemoryKey<MemoryNamespace.Value<"app/discussions", 1, string>>
key
:
const MemoryKey: {
Wire: typeof MemoryKeyWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryKey<Namespace>) => MemoryKey<Namespace>;
}
MemoryKey
.
make: <MemoryNamespace.Value<"app/discussions", 1, string>>(fields: MemoryKey<MemoryNamespace.Value<"app/discussions", 1, string>>) => MemoryKey<MemoryNamespace.Value<"app/discussions", 1, string>>
make
({
namespace: MemoryNamespace.Value<"app/discussions", 1, string>
namespace
,
id: string
id
:
work: PreparedActivity
work
.
workId: string & Brand<"@effect-agent/thread/Digest">
workId
}),
operationId: string

The same operation ID must always carry exactly the same Schema-encoded command.

operationId
:
work: PreparedActivity
work
.
workId: string & Brand<"@effect-agent/thread/Digest">
workId
,
expectedRevision: string | null
expectedRevision
: null,
locator: string
locator
: `memory://dan-discussions/${
work: PreparedActivity
work
.
workId: string & Brand<"@effect-agent/thread/Digest">
workId
}`,
content: MemoryContent
content
: {
...
const content: MemoryContent
content
,
metadata: {
readonly [x: string]: Schema.Json;
}
metadata
: { ...
const content: MemoryContent
content
.
metadata: {
readonly [x: string]: Schema.Json;
}
metadata
,
sourceRecordDigest: string & Brand<"@effect-agent/thread/Digest">
sourceRecordDigest
:
work: PreparedActivity
work
.
recordDigest: string & Brand<"@effect-agent/thread/Digest">
recordDigest
},
},
scopes: readonly (string & Brand<"@effect-agent/core/MemoryScope">)[]
scopes
: [
const MemoryScope: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">

Host-defined recall visibility label. A scope identifies access policy; it does not grant it.

MemoryScope
.
BottomWithoutNew<unknown, unknown, unknown, unknown, String, brand<NonEmptyString, "@effect-agent/core/MemoryScope">, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.make(input: string, options?: Schema.MakeOptions): string & Brand<"@effect-agent/core/MemoryScope">

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
("dan-approved-chad-and-tim")],
});
yield*
const writer: {
readonly change: <Namespace extends MemoryNamespace.Any>(write: MemoryWrite<Namespace>) => Effect.Effect<MemoryDocument<Namespace>, MemoryWriteError>;
}
writer
.
change: <MemoryNamespace.Value<"app/discussions", 1, string>>(write: MemoryWrite<MemoryNamespace.Value<"app/discussions", 1, string>>) => Effect.Effect<MemoryDocument<MemoryNamespace.Value<"app/discussions", 1, string>>, MemoryWriteError, never>
change
(
const command: MemoryWrite<MemoryNamespace.Value<"app/discussions", 1, string>>
command
);
});
const
const key: ActivityProcessorKey
key
=
class ActivityProcessorKey

Application-owned processor identity. Changing the version starts independent progress.

ActivityProcessorKey
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<ActivityProcessorKey, { readonly processorId: string; readonly processorVersion: string; readonly threadId: string; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly processorId: string;
readonly processorVersion: string;
readonly threadId: string & Brand<"@effect-agent/core/ThreadId">;
}, options?: Schema.MakeOptions): ActivityProcessorKey

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
({
processorId: string
processorId
: "discussion-statements",
processorVersion: string
processorVersion
: "1",
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
)("dan-chad"),
});
const
const limits: ActivityPassLimits
limits
=
class ActivityPassLimits
ActivityPassLimits
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<ActivityPassLimits, { readonly maxRecords: number; readonly pageSize: number; readonly timeoutMillis: number; readonly leaseMillis: number; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly maxRecords: number;
readonly pageSize: number;
readonly timeoutMillis: number;
readonly leaseMillis: number;
}, options?: Schema.MakeOptions): ActivityPassLimits

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
({
maxRecords: number
maxRecords
: 128,
pageSize: number
pageSize
: 16,
timeoutMillis: number
timeoutMillis
: 30_000,
leaseMillis: number
leaseMillis
: 31_000,
});
// Supply a unique owner for each worker lifetime and the application's Layers.
const
const ingest: (owner: string) => Effect.Effect<ActivityPassResult, Issue | MemoryWriteError | ActivityStoreFailure | ActivityProcessingError | DigestError | ThreadStoreError | ThreadNotMaterialized | Schema.SchemaError, MemoryWriter | ActivityProcessorStore | ThreadStore | Crypto>
ingest
= (
owner: string
owner
: string) =>
processCommittedActivity<Issue | Schema.SchemaError, never, MemoryWriteError | Schema.SchemaError, MemoryWriter>(processor: CommittedActivityProcessor<Issue | Schema.SchemaError, never, MemoryWriteError | Schema.SchemaError, MemoryWriter>): Effect.Effect<ActivityPassResult, Issue | MemoryWriteError | ActivityStoreFailure | ActivityProcessingError | DigestError | ThreadStoreError | ThreadNotMaterialized | Schema.SchemaError, MemoryWriter | ... 2 more ... | Crypto>

Process one bounded committed prefix of one application-selected Thread. Nothing runs until invoked, and no daemon, global Thread directory, admission ledger, or engine checkpoint is involved. PersistentHistory makes a successful Run's batch visible together; durable Runs expose incremental committed records. The application decides what each record means.

A durable pending output wins over a new extraction after restart. Apply finishes before progress advances, so separate output stores require durable idempotency. This does not promise exactly-once external side effects. Each callback owns a Scope; the finite pass has a timeout and a longer lease. Claim release has a separate 500ms deadline; failure leaves the bounded lease to expire.

processCommittedActivity
({
CommittedActivityProcessor<E = never, R = never, EApply = never, RApply = never>.key: ActivityProcessorKey
key
,
CommittedActivityProcessor<E = never, R = never, EApply = never, RApply = never>.owner: string

Unique to this worker lifetime; it is not canonical Thread ownership.

owner
,
CommittedActivityProcessor<E = never, R = never, EApply = never, RApply = never>.limits: ActivityPassLimits
limits
,
CommittedActivityProcessor<Issue | SchemaError, never, MemoryWriteError | SchemaError, MemoryWriter>.extract: (record: CanonicalRecordEnvelope) => Effect.Effect<Schema.Json, Issue | Schema.SchemaError, never>

Select eligibility and encode an application Schema; null can represent no output.

extract
,
CommittedActivityProcessor<Issue | SchemaError, never, MemoryWriteError | SchemaError, MemoryWriter>.apply: (work: PreparedActivity) => Effect.Effect<void, MemoryWriteError | Schema.SchemaError, MemoryWriter>

Must durably reconcile workId before returning, using atomic idempotency and conditional writes. This may run again after any lost acknowledgement. A stale invocation can finish only the already pinned output; it must not overwrite a later correction or withdrawal. Retain reconciliation evidence for the lifetime of pending work, including any supported backup/restore window. Returning before durable application would permit skipped output.

apply
});

The optional SQLite progress Layer can share a connection with the memory writer. Supply the host’s existing ThreadStore and Crypto Layer to the pass as well; the processor never uses Thread ownership epochs, SubmissionLedger, or engine checkpoints for its own progress.

import {
const activityProcessorStoreLayer: Layer.Layer<ActivityProcessorStore, SqliteActivityInitializationError, SqlClient>

SQLite activity progress with the production no-op mutation failpoint.

activityProcessorStoreLayer
} from "@yielded/agent-storage-sqlite/sqlite-activity-store";
import {
const memoryStoreLayer: Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, SqlClient>

SQLite memory reader, single writer and batch writer with the production no-op failpoint.

memoryStoreLayer
} from "@yielded/agent/sql-memory-store";
import {
import SqliteClient
SqliteClient
} from "@effect/sql-sqlite-node";
import {
import Layer
Layer
} from "effect";
const
const MemoryProcessing: Layer.Layer<ActivityProcessorStore | MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteActivityInitializationError | SqliteMemoryInitializationError, never>
MemoryProcessing
=
import Layer
Layer
.
const mergeAll: <[Layer.Layer<ActivityProcessorStore, SqliteActivityInitializationError, SqlClient>, Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, SqlClient>]>(layers_0: Layer.Layer<ActivityProcessorStore, SqliteActivityInitializationError, SqlClient>, layers_1: Layer.Layer<...>) => Layer.Layer<...>

Combines all the provided layers concurrently, creating a new layer with merged input, error, and output types.

When to use

Use when you need to combine multiple independent layers.

Details

All layers are built concurrently, and their outputs are merged into a single layer.

If multiple merged layers depend on the same layer value, that dependency is shared by default. Reuse a named layer value when you want services to share the same resource, such as one database pool.

Example (Merging independent layers)

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") {}
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const logs: Array<string> = []
const loggerLayer = Layer.succeed(Logger, {
log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => logs.push(msg)))
})
const mergedLayer = Layer.mergeAll(dbLayer, loggerLayer)
const program = Logger.use((logger) => logger.log("ready"))
Effect.runSync(Effect.provide(program, mergedLayer))
logs // => ["ready"]

@see ― merge for merging one layer with another layer or array

@category ― zipping

@since ― 2.0.0

mergeAll
(
const activityProcessorStoreLayer: Layer.Layer<ActivityProcessorStore, SqliteActivityInitializationError, SqlClient>

SQLite activity progress with the production no-op mutation failpoint.

activityProcessorStoreLayer
,
const memoryStoreLayer: Layer.Layer<MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteMemoryInitializationError, SqlClient>

SQLite memory reader, single writer and batch writer with the production no-op failpoint.

memoryStoreLayer
).
Pipeable.pipe<Layer.Layer<ActivityProcessorStore | MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteActivityInitializationError | SqliteMemoryInitializationError, SqlClient>, Layer.Layer<ActivityProcessorStore | MemoryReader | MemoryWriter | SqlMemoryBatchWriter, SqliteActivityInitializationError | SqliteMemoryInitializationError, never>>(this: Layer.Layer<...>, ab: (_: Layer.Layer<...>) => Layer.Layer<...>): Layer.Layer<...> (+21 overloads)
pipe
(
import Layer
Layer
.
const provide: <never, never, SqlClient | SqliteClient.SqliteClient>(that: Layer.Layer<SqlClient | SqliteClient.SqliteClient, never, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, E2, Exclude<RIn2, SqlClient | SqliteClient.SqliteClient>> (+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 SqliteClient
SqliteClient
.
const layer: (config: SqliteClient.SqliteClientConfig) => Layer.Layer<SqliteClient.SqliteClient | SqlClient>

Builds a layer from a node SQLite client configuration, providing both SqliteClient and the generic SqlClient service.

@category ― layers

@since ― 4.0.0

layer
({
SqliteClientConfig.filename: string
filename
: "memory.sqlite" })),
);

Every claim acquisition allocates a fresh fencing epoch, including reacquisition by the same owner after release. Expired or superseded workers cannot replace pending output or advance progress. A destination invocation already in flight may finish the saved output, so its own idempotency and conditional writes remain required. Extraction and application each own a Scope; the pass has a deadline and release gets at most another 500ms. If release fails, the lease expires. The failpoint-enabled Layer exposes initialization and each mutation before, inside, and after its transaction for recovery tests. SQLite rejects activity progress whose JSON exceeds 16,777,216 JavaScript string code units before writing, with ActivityStoreError reason invalid-input. Rejected writes leave prior progress intact.

ActivityProcessorStore.inspect exposes the per-processor, per-version, per-Thread cursor, pending work, and last advancement time. A successful pass reports its captured tail, through sequence, and remaining records in that prefix. These are per-Thread watermarks, not a global freshness promise. Process selected Threads with bounded Effect.forEach and handle each pass’s typed result independently when one failed Thread should not hold back others.

For the example workflow, the healthy commit-to-recallable target is 60 seconds. Hosts must measure that interval from the source commit to successful authoritative recall and choose a schedule that meets it. Recorded, extracted, advanced, indexed, and accessed times describe different events; none replaces the original activity time. An embedding index has its own progress and readiness, and must not advance this extraction cursor.

indexMemorySource and querySemanticMemory use the pinned upstream Effect AI EmbeddingModel. Supply its provider Layer directly. Direct loading, keyword retrieval, external attributed passages, and the cross-Thread workflow above need no embedding model or vector index. The framework does not define another provider abstraction or impose a ranking or aging policy.

The host binds SemanticMemoryProfile to the actual provider, model revision, dimensions, and chunking configuration. Rebuild when any of those change. Matching dimensions alone does not make two models compatible. Provider configuration overrides must not silently change the model behind a live index. Treat different preprocessing or precision settings as a new profile identity. Choose chunk sizes within the selected provider’s input limits. Read-only external sources can implement MemoryReader without a writer; sources with unknown revisions should use direct passage retrieval instead of this index.

import {
class SemanticIndexLimits
SemanticIndexLimits
,
class SemanticQueryLimits
SemanticQueryLimits
,
const indexMemorySource: <Namespace extends MemoryNamespace.Any>(key: MemoryKey<Namespace>, limits: SemanticIndexLimits) => Effect.Effect<SemanticIndexResult<Namespace>, MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex | Crypto>

Rebuild one selected source through the native Effect EmbeddingModel. The host binds the index profile to that provider's immutable model revision. A fresh Layer starts empty. Every invocation rebuilds; the host owns source discovery, scheduling, and freshness policy.

Chunking and embedding leave the last successful index intact. A final authoritative read rejects a source changed during embedding, then replace atomically exchanges its chunks. An independent source can still change between that read and replacement; querySemanticMemory always revalidates and excludes such stale candidates. No model or source text is attached to telemetry. Errors retain native provider/index types.

indexMemorySource
,
const querySemanticMemory: (query: string, access: MemoryAccess, limits: SemanticQueryLimits) => Effect.Effect<SemanticQueryResult, MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex>

Query an optional derivative index, then read each distinct source once. Attribution and metadata always come from that current source. Revision, generation, namespace, access, locator, and exact UTF-8 excerpt checks run before returning any passage. Older candidates are omitted rather than assigning their similarity score to changed text. Process candidates in source groups, retain only passage excerpts, and restore index rank. maxSourceBytes bounds aggregate UTF-8 JSON for distinct authorized sources with matching generation/revision/locator candidates. Its default is 16 MiB; exhaustion returns no partial result. Reader allocation, decoding, and one source serialization precede this bound.

Compose result.lookup through Memory.recall to enforce the final rendered item/byte/token budget and overall deadline. An empty result says nothing about undiscovered sources. Checks begun before an acknowledged correction/withdrawal may finish with their already captured source view.

querySemanticMemory
,
} from "@yielded/agent/semantic-memory";
import {
import Memory
Memory
,
import MemoryNamespace
MemoryNamespace
} from "@yielded/agent";
import {
type MemoryAccess<Namespace extends MemoryNamespace.Any = { readonly address: string & Brand<"@effect-agent/core/MemoryNamespaceAddress">; }> = Omit<MemoryAccessWire, "namespace"> & {
readonly namespace: Namespace;
}
const MemoryAccess: {
Wire: typeof MemoryAccessWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryAccess<Namespace>) => MemoryAccess<Namespace>;
}
MemoryAccess
} from "@yielded/agent/memory-revalidation";
import {
type MemoryKey<Namespace extends MemoryNamespace.Any = { readonly address: string & Brand<"@effect-agent/core/MemoryNamespaceAddress">; }> = Omit<MemoryKeyWire, "namespace"> & {
readonly namespace: Namespace;
}
const MemoryKey: {
Wire: typeof MemoryKeyWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryKey<Namespace>) => MemoryKey<Namespace>;
}
MemoryKey
,
type MemoryScope = string & Brand<"@effect-agent/core/MemoryScope">
const MemoryScope: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">

Host-defined recall visibility label. A scope identifies access policy; it does not grant it.

MemoryScope
} from "@yielded/agent/memory-store";
import {
class MemoryRecallLimits

Output bounds cover the complete rendered reference text, including citations and provenance.

MemoryRecallLimits
} from "@yielded/agent/memory-reference";
import {
class SemanticMemoryProfile

A host declaration of the exact embedding space and deterministic chunking policy.

SemanticMemoryProfile
} from "@yielded/agent/semantic-memory-index";
import {
const inMemorySemanticIndexLayer: (profile: SemanticMemoryProfile, capacity: InMemorySemanticIndexCapacity) => Layer<SemanticMemoryIndex, MemoryIndexError>

Scoped disposable semantic index. No persistent build or recovery state is retained.

inMemorySemanticIndexLayer
} from "@yielded/agent-storage-memory/memory-semantic-index";
import {
import Effect
Effect
,
import Schema
Schema
} from "effect";
// Keep this Layer alive across refreshes and queries. A new instance starts empty.
export const
const makeIndex: (profile: SemanticMemoryProfile) => Layer<SemanticMemoryIndex, MemoryIndexError, never>
makeIndex
= (
profile: SemanticMemoryProfile
profile
:
class SemanticMemoryProfile

A host declaration of the exact embedding space and deterministic chunking policy.

SemanticMemoryProfile
) =>
function inMemorySemanticIndexLayer(profile: SemanticMemoryProfile, capacity: InMemorySemanticIndexCapacity): Layer<SemanticMemoryIndex, MemoryIndexError>

Scoped disposable semantic index. No persistent build or recovery state is retained.

inMemorySemanticIndexLayer
(
profile: SemanticMemoryProfile
profile
, {
maxSources: number
maxSources
: 1_024,
maxChunks: number
maxChunks
: 8_192 });
const
const TeamMemory: {
name: "app/team-memory";
version: 1;
make: (identity: string) => MemoryNamespace.Value<"app/team-memory", 1, string>;
decode: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
}
TeamMemory
=
import MemoryNamespace
MemoryNamespace
.
const define: <"app/team-memory", 1, string, string>(options: {
readonly name: "app/team-memory";
readonly version: 1;
readonly identity: Schema.Codec<string, string, never, never>;
}) => {
name: "app/team-memory";
version: 1;
make: (identity: string) => MemoryNamespace.Value<"app/team-memory", 1, string>;
decode: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
}
define
({
name: "app/team-memory"
name
: "app/team-memory",
version: 1
version
: 1,
identity: Schema.Codec<string, string, never, never>
identity
:
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
,
});
const
const namespace: MemoryNamespace.Value<"app/team-memory", 1, string>
namespace
=
const TeamMemory: {
name: "app/team-memory";
version: 1;
make: (identity: string) => MemoryNamespace.Value<"app/team-memory", 1, string>;
decode: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
restore: (input: unknown) => Effect.Effect<Readonly<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryNamespace.MemoryNamespaceError, never>;
}
TeamMemory
.
make: (identity: string) => MemoryNamespace.Value<"app/team-memory", 1, string>
make
("team-a");
export const
const refresh: (key: MemoryKey<typeof namespace>) => Effect.Effect<SemanticIndexResult<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex | Crypto>
refresh
= (
key: MemoryKey<MemoryNamespace.Value<"app/team-memory", 1, string>>
key
:
type MemoryKey<Namespace extends MemoryNamespace.Any = { readonly address: string & Brand<"@effect-agent/core/MemoryNamespaceAddress">; }> = Omit<MemoryKeyWire, "namespace"> & {
readonly namespace: Namespace;
}
MemoryKey
<typeof
const namespace: MemoryNamespace.Value<"app/team-memory", 1, string>
namespace
>) =>
indexMemorySource<MemoryNamespace.Value<"app/team-memory", 1, string>>(key: MemoryKey<MemoryNamespace.Value<"app/team-memory", 1, string>>, limits: SemanticIndexLimits): Effect.Effect<SemanticIndexResult<MemoryNamespace.Value<"app/team-memory", 1, string>>, MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex | Crypto>

Rebuild one selected source through the native Effect EmbeddingModel. The host binds the index profile to that provider's immutable model revision. A fresh Layer starts empty. Every invocation rebuilds; the host owns source discovery, scheduling, and freshness policy.

Chunking and embedding leave the last successful index intact. A final authoritative read rejects a source changed during embedding, then replace atomically exchanges its chunks. An independent source can still change between that read and replacement; querySemanticMemory always revalidates and excludes such stale candidates. No model or source text is attached to telemetry. Errors retain native provider/index types.

indexMemorySource
(
key: MemoryKey<MemoryNamespace.Value<"app/team-memory", 1, string>>
key
,
class SemanticIndexLimits
SemanticIndexLimits
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<SemanticIndexLimits, { readonly maxSourceBytes: number; readonly maxChunks: number; readonly timeoutMillis: number; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly maxSourceBytes: number;
readonly maxChunks: number;
readonly timeoutMillis: number;
}, options?: Schema.MakeOptions): SemanticIndexLimits

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
({
maxSourceBytes: number
maxSourceBytes
: 262_144,
maxChunks: number
maxChunks
: 128,
timeoutMillis: number
timeoutMillis
: 30_000,
}),
);
export const
const recall: (query: string) => Effect.Effect<Memory.RecalledMemory, MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError | MemoryRecallError, MemoryReader | EmbeddingModel | SemanticMemoryIndex>
recall
= (
query: string
query
: string) =>
import Memory
Memory
.
recall<MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex>(sources: readonly Memory.MemoryRecallSource<MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex>[], limits: MemoryRecallLimits, estimateTokens?: ((text: string) => number) | undefined): Effect.Effect<...>
export recall

Read in declaration/ranking order and retain whole passages that fit. Essential sources must have a represented passage when they return matches, including exact duplicates. Explicit passage authorities qualify source identity across readers; absent authority is local to the reader declaration. Raw authorities stay in host passages, never rendered text. maxSources bounds both reader declarations and authority-qualified selected sources. No-match is successful even when essential. Optional unavailable/stale sources remain visible in outcomes. Nothing is cached. Admitted conflicting identities are rejected even when an earlier passage does not fit the output budget. maxInputBytes bounds cumulative JSON passage encodings before selection or identity retention; its default is 16 MiB. Exhaustion stops validation and returns no partial context. Reader-owned allocation and result decoding precede that input bound.

The default estimate is one token per UTF-8 byte. Supply the selected model's tokenizer for tighter selection. The engine independently enforces its full per-call context budget. The deadline owns a Scope, so temporary reader resources finalize on every exit path.

recall
(
[
{
MemoryRecallSource<E = never, R = never>.id: string
id
: "team-semantic",
MemoryRecallSource<E = never, R = never>.essential: boolean
essential
: false,
MemoryRecallSource<MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex>.read: Effect.Effect<{
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}, MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex>
read
:
function querySemanticMemory(query: string, access: MemoryAccess, limits: SemanticQueryLimits): Effect.Effect<SemanticQueryResult, MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex>

Query an optional derivative index, then read each distinct source once. Attribution and metadata always come from that current source. Revision, generation, namespace, access, locator, and exact UTF-8 excerpt checks run before returning any passage. Older candidates are omitted rather than assigning their similarity score to changed text. Process candidates in source groups, retain only passage excerpts, and restore index rank. maxSourceBytes bounds aggregate UTF-8 JSON for distinct authorized sources with matching generation/revision/locator candidates. Its default is 16 MiB; exhaustion returns no partial result. Reader allocation, decoding, and one source serialization precede this bound.

Compose result.lookup through Memory.recall to enforce the final rendered item/byte/token budget and overall deadline. An empty result says nothing about undiscovered sources. Checks begun before an acknowledged correction/withdrawal may finish with their already captured source view.

querySemanticMemory
(
query: string
query
,
const MemoryAccess: {
Wire: typeof MemoryAccessWire;
make: <Namespace extends MemoryNamespace.Any>(fields: MemoryAccess<Namespace>) => MemoryAccess<Namespace>;
}
MemoryAccess
.
make: <MemoryNamespace.Value<"app/team-memory", 1, string>>(fields: MemoryAccess<MemoryNamespace.Value<"app/team-memory", 1, string>>) => MemoryAccess<MemoryNamespace.Value<"app/team-memory", 1, string>>
make
({
namespace: MemoryNamespace.Value<"app/team-memory", 1, string>
namespace
,
scope: string & Brand<"@effect-agent/core/MemoryScope">
scope
:
const MemoryScope: Schema.brand<Schema.NonEmptyString, "@effect-agent/core/MemoryScope">

Host-defined recall visibility label. A scope identifies access policy; it does not grant it.

MemoryScope
.
BottomWithoutNew<unknown, unknown, unknown, unknown, String, brand<NonEmptyString, "@effect-agent/core/MemoryScope">, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.make(input: string, options?: Schema.MakeOptions): string & Brand<"@effect-agent/core/MemoryScope">

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
("participating-channels"),
}),
class SemanticQueryLimits
SemanticQueryLimits
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<SemanticQueryLimits, { readonly timeoutMillis: number; readonly maxCandidates: number; readonly maxScannedChunks: number; readonly minScore: number; readonly maxQueryBytes: number; readonly maxSourceBytes?: number | undefined; readonly maxOutputBytes?: number | undefined; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly timeoutMillis: number;
readonly maxCandidates: number;
readonly maxScannedChunks: number;
readonly minScore: number;
readonly maxQueryBytes: number;
readonly maxSourceBytes?: number | undefined;
readonly maxOutputBytes?: number | undefined;
}, options?: Schema.MakeOptions): SemanticQueryLimits

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
({
maxQueryBytes: number
maxQueryBytes
: 8_192,
maxCandidates: number
maxCandidates
: 16,
maxScannedChunks: number
maxScannedChunks
: 8_192,
minScore: number
minScore
: 0.35,
timeoutMillis: number
timeoutMillis
: 1_000,
}),
).
Pipeable.pipe<Effect.Effect<SemanticQueryResult, MemoryStorageError | SemanticMemoryError | AiError | MemoryIndexError, MemoryReader | EmbeddingModel | SemanticMemoryIndex>, Effect.Effect<{
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}, MemoryStorageError | ... 2 more ... | MemoryIndexError, MemoryReader | ... 1 more ... | SemanticMemoryIndex>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)
pipe
(
import Effect
Effect
.
const map: <SemanticQueryResult, {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}>(f: (a: SemanticQueryResult) => {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}) => <E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

Transforms the value inside an effect by applying a function to it.

When to use

Use to transform an effect's success value with a function that returns a plain value, producing a new effect without changing the original effect's typed error or context requirements.

Details

map takes a function and applies it to the value contained within an effect, creating a new effect with the transformed value.

It's important to note that effects are immutable, meaning that the original effect is not modified. Instead, a new effect is returned with the updated value.

Example (Choosing map syntax variants)

import { Effect, pipe } from "effect"
const output: Array<unknown> = []
const myEffect = Effect.succeed(1)
const transformation = (n: number) => n + 1
const mappedWithPipe = pipe(myEffect, Effect.map(transformation))
const mappedWithDataFirst = Effect.map(myEffect, transformation)
const mappedWithMethod = myEffect.pipe(Effect.map(transformation))
void output.push(Effect.runSync(Effect.all([
mappedWithPipe,
mappedWithDataFirst,
mappedWithMethod
])))
output // => [[2, 2, 2]]

Example (Adding a service charge)

import { Effect, pipe } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const finalAmount = pipe(
fetchTransactionAmount,
Effect.map(addServiceCharge)
)
await Effect.runPromise(finalAmount) // => 101

@see ― mapError for a version that operates on the error channel.

@see ― mapBoth for a version that operates on both channels.

@see ― flatMap or andThen for a version that can return a new effect.

@category ― mapping

@since ― 2.0.0

map
((
result: SemanticQueryResult
result
) =>
result: SemanticQueryResult
result
.
lookup: {
readonly _tag: "Found";
readonly passages: readonly MemoryPassage[];
} | {
readonly _tag: "NoMatch";
} | {
readonly _tag: "Unavailable";
readonly message: string;
} | {
readonly _tag: "InsufficientFreshness";
readonly message: string;
}
lookup
)),
},
],
class MemoryRecallLimits

Output bounds cover the complete rendered reference text, including citations and provenance.

MemoryRecallLimits
.
BottomWithoutNew<unknown, unknown, unknown, unknown, Declaration, decodeTo<declareConstructor<MemoryRecallLimits, { readonly timeoutMillis: number; readonly maxSources: number; readonly maxItems: number; readonly maxBytes: number; readonly maxTokens: number; readonly maxInputBytes?: number | undefined; }, readonly [...], { ...; }>, Struct<...>, never, never>, ... 8 more ..., "required">.make(input: {
readonly timeoutMillis: number;
readonly maxSources: number;
readonly maxItems: number;
readonly maxBytes: number;
readonly maxTokens: number;
readonly maxInputBytes?: number | undefined;
}, options?: Schema.MakeOptions): MemoryRecallLimits

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
({
maxSources: number
maxSources
: 8,
maxItems: number
maxItems
: 8,
maxBytes: number
maxBytes
: 16_384,
maxTokens: number
maxTokens
: 4_096,
timeoutMillis: number
timeoutMillis
: 1_000,
}),
);

Provide the same index instance, MemoryReader, and native EmbeddingModel to both operations. Refresh also requires Effect Crypto. The provider Layer owns its resources; the index belongs to its Layer’s Scope. Captured index methods fail after that Scope closes. Put recall in the transient-context hook above. The final envelope, including attribution and citations, must fit Memory.recall’s item, UTF-8 byte, and token limits; the engine separately admits the full prompt. essential: false permits explicitly returned unavailable outcomes. It does not swallow errors. Map only intended expected failures to an Unavailable lookup in application policy. SemanticQueryLimits.maxOutputBytes bounds aggregate UTF-8 JSON passage output before retention, including repeated attribution and metadata. It defaults to 16 MiB, accepts at most 64 MiB, and fails with SemanticMemoryError reason budget. Source-byte limits count each distinct source once; they do not substitute for this output bound or the final rendered recall limits.

Chunking greedily packs complete Unicode codepoints up to maxChunkBytes. It neither summarizes nor silently drops a source suffix. Chunk IDs include a digest of the whole profile and the ordinal; candidates also carry source identity, revision, generation, and byte offsets. Indexing checks source-byte and chunk-count limits before calling the provider. Returned vectors must match the profile and have a positive finite norm. This simple chunker is a deterministic baseline; it makes no sentence-boundary or relevance promise.

inMemorySemanticIndexLayer is a replaceable exact cosine adapter. It bounds all registered source keys, including withdrawal tombstones, and all ready chunks. A search over its scan limit fails instead of ranking an arbitrary prefix. Scores tie by source ID, revision, and chunk ordinal. Configuration rejects maxChunks * profile.dimensions above 16,777,216 vector components before allocating index state. For a 4,096-dimensional profile, set maxChunks to at most 4,096. This bounds stored vectors, not total process memory or allocations made by callers. maxSourceBytes caps the aggregate UTF-8 JSON of retained source identities, including terminal tombstones. It defaults to 16 MiB and accepts at most 64 MiB. Replacement and withdrawal check this bound atomically before changing the index. A rejected change leaves the prior source and chunks intact; a smaller replacement releases identity capacity. Replaying a withdrawal does not charge the same tombstone twice. Map keys, object overhead, and vector storage are not included in this source-encoding budget. The adapter holds no authoritative documents or attribution.

Refresh prepares chunks and embeddings before calling SemanticMemoryIndex.replace. Replacement validates the profile and source revision, then exchanges all chunks atomically. Failed or cancelled refreshes leave the last successful index intact. Older generations and divergent same-generation identities are fenced. Withdrawal is terminal within the instance and blocks delayed replacements. The index exposes no build epochs, publication states, inspection, or mutation failpoints. Closing and recreating the Layer discards all chunks and tombstones; rebuild from current authoritative sources before requiring complete semantic recall.

The index and source are independent. Refresh reads the source again before publication, but a change can still occur between that read and the index write. Every query therefore rereads each candidate source, checks namespace, access, revision, generation, locator, and exact excerpt, and takes attribution and metadata from that source. Stale candidates are omitted; their old score is never assigned to corrected text. A source correction may temporarily reduce recall until refresh finishes. Missing, withdrawn, or revoked sources cannot pass checks begun after the authoritative change. Already captured views may finish, as described under withdrawal. Returned passages bind their private authority to the query’s authorized namespace, so downstream recall can combine independent namespaces without confusing their source IDs. Recall renders only opaque authority labels.

Queries group candidates by source, read each source once, and restore the original index ranking after validation. Full source documents and their excerpt-check encodings are local to one group. SemanticQueryLimits.maxSourceBytes separately bounds the aggregate UTF-8 JSON of distinct authorized sources with generation, revision, and locator matches. It defaults to 16 MiB and can be set up to 64 MiB. Missing, withdrawn, unauthorized, and identity-stale sources are excluded without consuming this budget. Exceeding it returns a typed budget error with no partial result. Reader allocation, decoding, and one source’s serialization precede this check; it is not a whole-process heap limit. The indexing limit with the same name bounds one source’s text instead.

SemanticQueryResult reports scanned chunks, excluded stale and unauthorized candidates, query embedding usage when the provider supplies it. It makes no completeness claim; the host owns discovery, refresh scheduling, and required freshness. An empty index or no-match result does not prove the corpus has no relevant memory. No source text, query text, attribution, or vectors are attached to the helpers’ Effect spans.

Deadlines interrupt cooperative work and run finalizers. A provider that cannot cancel native I/O may need to drain its active call before finalizing; account for that in the host’s latency policy. The reproducible tooling/semantic-memory-eval consumer compares direct, lexical, and real local embedding recall on a frozen synthetic corpus. It separates warm query latency, cached-file cold model startup, source-commit-to-recallable lag, background extraction/indexing, and injected slow or failed requests. Its declared targets are 250 ms warm added recall, 3 seconds with a cold model instance and cached files, and 60 seconds from a healthy source commit to recall. These are example targets, not provider or production guarantees. The example reports misses and contradictory retrievals; applications must choose their own decision, commitment, and aging policies.

Every application tool result, including MCP output, passes through toolResultBounds once before history or durable storage. Results within the limit keep their encoded bytes. Larger results use one canonical envelope:

{
"truncatedToolResult": true,
"originalBytes": 412887,
"head": "...first half of the byte budget...",
"tail": "...last half..."
}

The model and journal see the same envelope. Replay therefore stays consistent. The default limit is 50 KiB. Provider-executed tool results are exempt because the provider has already put them in the response.

With runStatus: "appended", each outgoing request ends with a derived status line:

<run-status>turn 3/12 · tool-calls 11/24 · tokens 84210/200000 · research-remaining 83790 · completion-reserve 32000 · last-context 23480 · elapsed 74s/300s</run-status>

At 80 percent of a limit, the line asks the model to wrap up. The token warning uses the research balance after reserving completion capacity. The runtime also warns when that balance cannot cover another input as large as the last call.

runStatus defaults to "off". The optional status line is built for each request and never enters canonical history. With chronological adapters, it is trailing system/developer guidance, leaving the retained user/tool message available as a cache boundary. Other adapters receive it as a trailing user message; account for their cache-boundary behavior when enabling it. Provider cache settings remain host-owned, and changing other prompt content can still prevent reuse. Host-enforced limits and BudgetWarning events remain active with either setting.

Crossing 80 percent emits one BudgetWarning event for that dimension. Turn, tool call, and token exhaustion follow onExhaustion.

With "final-answer", an over-budget tool batch runs no handlers. The next request forbids tool use, except for the definition’s singleton completion tool. Turn exhaustion allows one grace turn. Token exhaustion completes from the breaching response when it already contains decodable output; otherwise it allows the same single constrained turn.

The result and RunCompleted event report finishReason: "budget-exhausted". Their exhausted field names the limit: "tokens", "turns", or "tool-calls".

Delegated child results carry the same marker through SubagentCompleted.exhausted and projectResult. onExhaustion: "fail" rejects work after the breach. Duration and cost breaches always fail because another model call would add time or cost.

With contextTokenLimit, the engine estimates the next prompt before every turn. Ordinary append-only history starts from the last provider-reported input and estimates appended content. Preparation and transient-context hooks use a fresh estimate. Full estimates exclude repeated system messages removed from the outgoing request. Within a turn, the engine reuses the history view and estimate until compaction changes them. The default compactor then:

  1. clears old application tool results outside the preferred keepRecentTokens tail while keeping message structure and call/result pairs;
  2. if pruning is insufficient, makes one metered summary call and keeps the instruction prefix, summary, and recent tail. The retained tail can exceed keepRecentTokens to keep user inputs with their replies and tool calls with their results.

Compaction changes the model view. It never rewrites the thread log. CompactionPerformed reports each reduction. DN and DC also append CompactionCreated, so later attempts and runs use the same compacted view.

A summary must finish successfully and contain non-whitespace text. The interpreter charges its usage before validating it. A rejected summary leaves the previous summary and coverage in place; already committed pruning remains. All summaries, including custom strategy decisions, are limited to 65,536 characters and fail with CompactionError above that bound. Summaries are never silently truncated to fit.

The complete default summarizer request fits within 80,000 characters, including instructions, transcript delimiters, and the previous summary. It retains the oldest and newest covered messages and marks the omitted middle. Message and string-result previews are clipped before rendering; oversized structured tool results receive an explicit omission marker. An oversized previous summary is rejected before rendering. These limits change only the model view, leaving canonical evidence intact.

If the provider reports context overflow, the engine may compact and retry once. Transport ambiguity can duplicate that model call. A second rejection, or overflow without a definition context limit or resolved modelCall allowance, fails as ContextOverflowError.

Install a ContextCompactor Layer to change the strategy, estimator, or summary model. The default is ContextCompactor.layer. All AgentRuntime entry points also need a Thread history policy.

const compactorLayer = ContextCompactor.layerWithModel(summaryModel);
const result = AgentRuntime.run(agent, input).pipe(Effect.provide(compactorLayer));

The summary model’s Layer requirements stay visible. Its usage is charged under that model’s provider and name.

A custom compact implementation emits CompactionDecision values. Each decision covers an exclusive source prefix and clears old tool results, supplies a summary, or starts a fresh context window. The interpreter rejects cuts through tool pairs, changes to protected instructions or input, decisions that make no progress, and more than one prune followed by one replacement in a turn. request.trigger distinguishes pressure, overflow, and an explicit requested rollover; request.modelCallAllowed tells the strategy whether a separate summary call is admitted. Summary calls must use request.summarize so metering, response limits, and the run deadline still apply.

estimate must return a non-negative finite integer. Strategy failures use CompactionError. Defects and interruption retain their Effect meaning.

Durable coordinators map the covered prefix to complete canonical records before committing a decision. Summarization covers prior-run records. Pruning and rollover can also cover settled batches inside the current run, preserving its original instructions and input. A transform or decision that cannot map cleanly fails before the view changes. The canonical log remains append-only.

A completed Tool batch enters official history before the repeated-failure limit ends a Run, including provider-executed results. A later Run may cover an incomplete prior-Run batch already omitted from its prompt only when canonical records prove that prior Run ended after its final response. This changes coverage eligibility without settling, rewriting, or replaying the old call. Current-Run, nonterminal, and malformed post-terminal batches remain protected.

Provide ContextCompactor directly to the durable host Layer. In this example, HostLive is your application’s assembled host Layer:

import { ContextCompactor } from "@yielded/agent/context-compactor";
import { OpenAiLanguageModel } from "@effect/ai-openai";
import { Layer } from "effect";
export const CompactorLive = ContextCompactor.layerWithModel(
OpenAiLanguageModel.model("gpt-6-luna"),
);
export const RuntimeLive = HostLive.pipe(Layer.provide(CompactorLive));

Provide the summary model’s client to CompactorLive. The same composition works with the Node host Layer, Cloudflare application layer, or custom runtime assembly. To use a prompt transform and a custom compactor together, provide RunContextPreparation and ContextCompactor independently. The runtime captures both when its Layer is acquired and retains them across replacement attempts. Providing a different compactor around a worker call does not replace the host’s choice.

ContextCompactor.layerRollover starts a fresh window under context pressure or on the one allowed provider-overflow retry. It makes no summary-model call. The engine retains the original instructions and input, inserts a window marker and bounded recovery excerpts, and keeps trailing user steering verbatim. A rollover changes the prompt within the same run; turn, tool, duration, and spending limits continue accumulating. An automatic rollover that cannot reduce the prompt or fit its handoff fails with CompactionError; admission still checks the final prompt against contextTokenLimit.

For model-directed control, include the native ContextTools.toolkit and its handlers:

import { ContextTools } from "@yielded/agent";
import { ContextCompactor } from "@yielded/agent/context-compactor";
import { ThreadContextHistory } from "@yielded/agent";
import { Layer } from "effect";
const tools = ContextTools.toolkit;
const toolHandlers = ContextTools.layer;
const compactor = ContextCompactor.layerRollover;
const history = ThreadContextHistory.layer({ maxRecords: 16_384 });
// Supply your authorized ThreadStore to history, then provide it at the Run boundary.
const contextServices = Layer.merge(compactor, history);

Merge tools into the Agent’s toolkit and provide toolHandlers when building the Agent. Install compactor directly to the durable host Layer, as shown above. Supply history where the registered Agent’s tool services are provided. It depends on the host’s ThreadStore; an ephemeral application can implement the ContextHistory port over its retained transcript.

Tool Behavior
new_context({ handoff? }) Requests a rollover before the next turn. Call it alone; a short handoff is optional.
get_context_remaining({}) Returns window identity and estimated live tokens. Unconfigured capacity is null.
search_context_windows({ query, limit?, beforeRecordId? }) Searches retained evidence newest first; returns at most three record snippets per page.
read_context_window({ recordId, offset?, maxChars? }) Reads up to 5,000 characters; use nextOffset to continue.

History search matches one literal substring, after trimming surrounding whitespace and JavaScript case folding. It does not interpret multiple keywords, AND/OR, wildcards, regular expressions, or quotes as query syntax. Search for a short exact phrase, document label, or identifier: "RECEIPTS dock-03" only finds those characters together; "dock-03" finds that label wherever it occurs in eligible text.

Recent search calls and notes can themselves match. To reach older records, repeat the query with beforeRecordId set to the last hit’s recordId. The anchor and every newer canonical position are excluded. Continue until the result contains fewer than the requested limit (default three), including an empty array. A full final page needs one more request to see the empty page. For example, these model tool calls use an illustrative returned ID:

search_context_windows({ query: "dock-03", limit: 3 });
// If the last hit has recordId "record:42":
search_context_windows({ query: "dock-03", limit: 3, beforeRecordId: "record:42" });
// Once an original source is found, read its recordId with read_context_window.

ContextHistory.search keeps its existing hit-array result and one-to-twenty result limit; existing callers can omit the new optional field. IDs are opaque, not sortable positions or authorization capabilities. An anchor must be eligible retained evidence in the current Thread, but need not match the query. An unknown, removed, foreign, or non-evidence anchor returns ContextHistoryError with reason not-found; malformed parameters return invalid-input. Each request captures a fresh tail and checks current authorization. New appends cannot push older matches out of a continued page, but pages do not share a retained snapshot or bypass retention. A scan, deadline, or index-work limit is an explicit failure, never an exhausted page. ThreadContextHistory searches backwards from the captured tail or exact anchor in eight-record pages. maxRecords limits work per search, regardless of Thread age; finding a full page can stop early. Exact reads and window ownership use native locators across archived ranges. Storage verification separately checks global integrity. Search snippets remain at most 2,000 UTF-16 characters each; text reads use their existing bounds. Every continuation is another Tool call charged to the Run’s ordinary cumulative limits.

Both the default compactor and layerRollover honor an explicit new_context request. Custom strategies must emit its requested rollover and cutoff. The engine recognizes the trusted Tool annotation, never the tool’s name. Mixed batches and programmatic broker calls are rejected before handlers start. Failed tool results do not trigger rollover. A successful request is recovered from its canonical result if ownership is lost before the boundary is written; after the boundary is written, recovery uses the saved window without replaying covered tools.

The optional handoff is saved with the rollover boundary and included in the next model prompt; it does not require separate notes tools. It is limited to 20,000 characters and 32 KiB of JSON-encoded UTF-8. Automatic handoffs are smaller deterministic excerpts of covered user messages and the last tool batch; they may omit older progress and do not claim that external actions succeeded. Notes and history are untrusted working evidence. Verify live state before repeating an action.

MemoryNotes.toolkit supplies read_notes and write_notes. Bind MemoryNotes.layer to one host-selected MemoryKey, locator, attributions, and scopes, then supply your existing MemoryReader, MemoryWriter, and Effect AI IdGenerator.IdGenerator. For default operation identities, provide Layer.succeed(IdGenerator.IdGenerator, IdGenerator.defaultIdGenerator) from effect/ai. Notes are a full document replacement with expectedRevision; conflicts require reading and merging again. Durable Steps retain the exact write command and operation identity for recovery. Notes survive a process restart only when the selected Memory store does. The model cannot choose a filesystem path, another memory key, or another thread through these tools.

When using MemoryNotes, tell the Agent to save important state before new_context, read its notes after rollover, and use history to verify details. Notes are optional and independent of window transitions; the framework does not synthesize or overwrite them automatically. This works with any model that can use the native tools.

The canonical history adapter scans a fixed tail in bounded pages, with a default 10-second deadline. It fails explicitly when the configured scan limit is exceeded. It exposes model-visible text, tool calls, and retained tool results, excluding system instructions and operational records. It can retrieve evidence removed by compaction, but cannot recover tool bytes discarded by result bounds, transient references, or records removed by a separate retention policy. Tightening Tool result bounds below these tools’ maximum payloads may truncate their results too.

For an indexed adapter, use @yielded/agent/thread-context-history-projection to project each canonical record into eligible retained text or a rollover boundary. Its query normalization and snippet matching preserve the native literal, case-folded search semantics. Operational records still consume their canonical sequence even when their projection is empty.

Commit each contiguous projection batch and its watermark atomically. Window membership uses coversThrough, which may precede the boundary’s own record: a later rollover can relabel evidence already in the index. Capture one canonical tail for each lookup and restrict both evidence and boundary records to it. Do not return partial results when the index has not covered that prefix. The index supplies candidate identities and sequences; recheck host authorization and reread each selected canonical record before returning evidence. A known sequence permits a single ThreadStore.read with afterSequence: sequence - 1 and limit: 1, followed by identity checks.

Custom ContextHistory adapters, including application-owned Cloudflare indexes, must implement beforeRecordId before using the updated native search tool. Resolve the anchor in the authorized Thread and captured tail, reread its canonical record, and check its identity and evidence eligibility. Then select literal matches with sequence < anchor.sequence, ordered by descending sequence, up to the requested limit. Keep boundary lookup bounded by the captured tail, not the anchor: a later rollover commit can assign older evidence to a window. Reverify each selected source and its literal match. Bound index catch-up, candidate work, result bytes, and deadlines; fail explicitly when a complete page cannot be established. Do not emulate this with an offset into a changing result set, silently ignore the anchor, or truncate candidates before matching. An adapter awaiting this update must reject anchored requests with unavailable rather than returning the first page again. The built-in ThreadContextHistory.layer implements the contract over every ThreadStore; its default scan ceiling and deadline are unchanged, and storage adapters need no persisted-format migration.

An evidence index supplies retrieval candidates; canonical facts and the submission ledger own recovery. A Run continuation references the exact original input, saved context, and declared operations. Compaction changes model context without changing those execution facts or accumulated charges. Selected recovery reads a bounded Run prefix and suffix; unavailable or corrupt required evidence fails typed and leaves the work owed. Initial context assembly and complete history export have their own costs and storage bounds.

@yielded/agent also has an application-managed data path. prepareModelContext derives bounded text from a Thread.Thread. digestCompactionSource binds a CompactionArtifact to that source. applyCompaction validates the artifact before replacing covered view messages with its summary. The application creates, stores, and applies the artifact.

RetainedFact values remain artifact metadata. They do not enter the prompt or a separate memory store automatically. This path is separate from ContextCompactor; the interpreter does not call applyCompaction. Memory.recall is the optional read path for application-owned sources; it does not persist passages or turn compaction artifacts into memory.

The budget snapshot separates cumulative and live context usage:

const report = Effect.gen(function* () {
const usage = yield* budget.snapshot;
usage.inputTokens;
usage.cacheReadInputTokens;
usage.cacheWriteInputTokens;
usage.lastInputTokens;
usage.lastOutputTokens;
});

Watch lastInputTokens for current context pressure. inputTokens is cumulative and grows on every call. Provider caching may lower its cost. See Run & stream for hook setup.

  • Leave output and summary room under the model window. For a 200k window, start with a contextTokenLimit between 150k and 170k.
  • keepRecentTokens defaults to 20k. Pruning retains this preferred tail while the full prompt fits its target; under pressure it clears additional older results. The newest tool result always stays verbatim. If the remaining prompt cannot fit, the configured summary or overflow policy applies.
  • Use tokenBudget as a runaway limit. Use costBudgetMicrousd to bound estimated spend.
  • Delegate noisy research to bounded children so their raw tool output stays out of the parent context.