Skip to content

Common builder stacks

Use this page to assemble realistic builder chains. For every method, default, and env var, see the authoritative references:

  • Builder API — signatures, option types, ReactiveAgent methods, events, and AgentResult.
  • Configuration — grouped checklist of builder methods and high-level defaults.

For a first end-to-end walkthrough, see Quickstart and Your first agent.

  1. Start from ReactiveAgents.create() — default name is "agent", default provider is "test" until you call .withProvider(...).
  2. Finish with .build() (async) or .buildEffect() (Effect) — see Effect-TS primer.
  3. Dispose agents that use MCP stdio or other subprocess tools: prefer await using, runOnce(), or dispose()Resource management.
  4. Custom tools and hooks return Effectimport { Effect } from "effect" and use Effect.succeed / Effect.fail / Effect.gen as needed.

Stack A — Direct LLM (no reasoning loop)

Section titled “Stack A — Direct LLM (no reasoning loop)”

Single-shot Q&A; no tools, no multi-step loop. Smallest surface area.

import { ReactiveAgents } from "reactive-agents";
await using agent = await ReactiveAgents.create()
.withName("qa-bot")
.withProvider("anthropic")
.withModel("claude-sonnet-4-20250514")
.build();
const result = await agent.run("Explain what a functor is in one paragraph.");
console.log(result.output);

Enables the reasoning kernel and the default tool registry (file I/O, web search when keys exist, etc.). With .withTools(), Conductor meta-tools (brief, find, pulse, recall) default on unless you pass .withMetaTools(false) — see Tools and Builder API — MetaToolsConfig.

import { ReactiveAgents } from "reactive-agents";
await using agent = await ReactiveAgents.create()
.withName("tool-agent")
.withProvider("anthropic")
.withModel("claude-sonnet-4-20250514")
.withReasoning()
.withTools()
.build();
const result = await agent.run("Use web-search to find today's date in UTC and reply with one sentence.");
console.log(result.output);

Stack C — Memory + reasoning + debrief context

Section titled “Stack C — Memory + reasoning + debrief context”

.withMemory() uses the standard tier by default (SQLite + FTS5; no embedding API required). Use { tier: "enhanced" } when you want vector similarity (embedding provider + env). Debrief-style artifacts are tied to memory + reasoning — details in Debrief & chat and Memory.

import { ReactiveAgents } from "reactive-agents";
await using agent = await ReactiveAgents.create()
.withName("researcher")
.withProvider("anthropic")
.withModel("claude-sonnet-4-20250514")
.withMemory() // or .withMemory({ tier: "enhanced" })
.withReasoning()
.withTools()
.build();
const result = await agent.run("Summarize the project goals in three bullets.");
if (result.debrief) console.log(result.debrief.summary);

Guardrails toggle injection / PII / toxicity detectors (all default on when guardrails are enabled). Observability drives the metrics dashboard at normal+ verbosity. Cost tracking enforces USD budgets when you pass limits.

import { ReactiveAgents } from "reactive-agents";
await using agent = await ReactiveAgents.create()
.withName("production-shape")
.withProvider("anthropic")
.withModel("claude-sonnet-4-20250514")
.withReasoning()
.withTools()
.withGuardrails({ toxicity: true, injection: true, pii: true })
.withObservability({ verbosity: "normal", live: false })
.withCostTracking({ perRequest: 0.25, daily: 10 })
.build();
await agent.run("Draft a short status update for the team.");

.withStreaming() sets the default density for agent.runStream() (tokens vs full). You can override per call. See Streaming and Streaming responses.

import { ReactiveAgents } from "reactive-agents";
await using agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withModel("claude-sonnet-4-20250514")
.withReasoning()
.withStreaming({ density: "tokens" })
.build();
for await (const event of agent.runStream("Write a haiku about TypeScript.")) {
if (event._tag === "TextDelta") process.stdout.write(event.text);
if (event._tag === "StreamCompleted") console.log("\nDone.");
}

Stack F — Agent as Data (serialize / restore)

Section titled “Stack F — Agent as Data (serialize / restore)”

toConfig() captures the builder state as AgentConfig. Use agentConfigToJSON / agentConfigFromJSON (from reactive-agents) for strings. Some runtime-only fields (e.g. custom ICS functions) are not round-tripped — see Builder API — Agent as Data.

import {
ReactiveAgents,
agentConfigToJSON,
agentConfigFromJSON,
} from "reactive-agents";
const builder = ReactiveAgents.create()
.withName("saved-agent")
.withProvider("anthropic")
.withModel("claude-sonnet-4-20250514")
.withReasoning()
.withTools();
const json = agentConfigToJSON(builder.toConfig());
const restored = await ReactiveAgents.fromJSON(json);
await using agent = await restored.build();
await agent.run("Ping.");

If defaultStrategy is "adaptive", you must set adaptive: { enabled: true }Reasoning, Builder API — ReasoningOptions.

import { ReactiveAgents } from "reactive-agents";
await using agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withModel("claude-sonnet-4-20250514")
.withReasoning({
defaultStrategy: "adaptive",
adaptive: { enabled: true },
})
.withTools()
.build();
await agent.run("Plan then execute: list two pros and two cons of serverless agents.");
TopicPage
Custom toolsBuilding tools
Streaming detailsStreaming responses
TestsTesting agents
Multi-agentMulti-agent patterns
GoalGuide or reference
Lifecycle hooksHooks
MCP serversBuilder API — MCP
Sub-agentsSub-agents
Effect compositionEffect-TS primer