@pleach/recipes
One-line factories that compose @pleach/core with sibling SKUs for the four most common consumer use cases — chatbot, RAG, observability, compliance — plus an instrumented coding agent.
@pleach/recipes is the use-case-targeted composition layer over
@pleach/core. Each subpath export is a one-line factory that
returns a wrapper around a real
SessionRuntime. Reach into bot.runtime
when you need the full primitive surface; reach for the recipe
when the boilerplate is what's in the way.
Each recipe lives at its own subpath so consumers only pay the import cost for what they use.
npm install @pleach/recipes @pleach/coreOptional peers — install only the ones a recipe needs:
npm install @pleach/observe # for observability + coding-agent recipes
npm install @pleach/compliance # for compliance recipe (v0.2)
npm install @pleach/coding-agent # for instrumentedCodingAgent
npm install @pleach/sandbox # for instrumentedCodingAgentsimpleChatbot
A minimal conversational agent. Composes only @pleach/core; no
sibling peers required. The factory lazily creates a session on
first ask() and reuses it on subsequent calls so a single
simpleChatbot instance holds conversational state.
import { simpleChatbot } from "@pleach/recipes/chatbot";
import { setOrchestratorAdapterCtor } from "@pleach/core/runtime";
import { MyOrchestratorAdapter } from "./my-provider"; // your adapter
// Register the OrchestratorAdapter ctor once at app boot. The recipes
// package does NOT bundle a provider — supply your own (OpenAI,
// Anthropic, OpenRouter, BYOK, etc.).
setOrchestratorAdapterCtor(MyOrchestratorAdapter);
const bot = simpleChatbot({
systemPrompt: "You are a helpful assistant.",
orchestratorConfig: {
provider: "openai",
model: "gpt-4o-mini",
apiKey: process.env.OPENAI_API_KEY!,
},
});
console.log(await bot.ask("hello"));
console.log(await bot.ask("what did I just say?"));When orchestratorConfig is omitted, ask() resolves to the
substrate placeholder ("This is a placeholder response from the harness runtime."). Supplying orchestratorConfig routes the call
through your registered OrchestratorAdapter.
ask() returns the final assistant text. It collects the
message.delta and message.complete events from
runtime.executeMessage() and concatenates the deltas; streaming
consumers should reach into bot.runtime directly.
A single bot instance holds the session across ask() calls.
Call await bot.reset() (or its alias await bot.newSession()) to
discard the memoized session id; the next ask() lazily creates a
fresh session via runtime.createSession(). If the underlying
stream emits an { type: "error" } event or throws, ask()
rejects with a ChatStreamError and preserves the original
failure on error.cause — branch on cause to distinguish
provider errors from abort signals from network timeouts. The same
session API and error type are inherited by ragChatbot,
observableChatbot, and compliantChatbot.
Reach for this when you want a working chatbot in under ten lines and don't yet need retrieval, observability, or compliance.
ragChatbot
A chatbot plus retrieval stage. The consumer supplies the
Retriever — the recipe is storage-agnostic on purpose; it does
not bind a vector store.
import { ragChatbot } from "@pleach/recipes/rag";
const bot = ragChatbot({
retriever: async (query, opts) =>
myVectorDb.search(query, { topK: opts?.topK ?? 4 }),
});
console.log(await bot.ask("what's our refund policy?"));Per turn, ask() calls the retriever, prepends the top-K chunks
to the user message as context with stable [N] indices for
citation, and delegates to the underlying simpleChatbot. When
the retriever returns no chunks or throws, the message passes
through unmodified — the bot stays useful if retrieval is down.
Reach for this when answers must be grounded in your documents and you already have a vector store or full-text index. Bring your own retrieval; the recipe stays out of that decision.
observableChatbot
A chatbot wrapped with @pleach/observe sub-agent attribution.
Every recordCall(...) issued during ask() inherits the outer
subagent path — no recorder argument threaded through call
sites.
import { init } from "@pleach/observe";
import { memory } from "@pleach/observe/destinations";
import { observableChatbot } from "@pleach/recipes/observability";
const dest = memory();
init({ destination: dest });
const bot = observableChatbot({
serviceName: "my-app-chatbot",
});
await bot.ask("hello");
console.log(dest.rows); // rows tagged subagent: "my-app-chatbot"@pleach/observe is an optional peer. If it's not installed,
ask() still works as a plain chatbot — instrumentation just
doesn't fire. Set serviceName: false to disable the ALS scope
when composing inside a buyer's outer scope.
Reach for this when traces, prompt/completion logs, and cost attribution are required on every turn and the boilerplate of threading the recorder through call sites isn't earning its keep.
compliantChatbot
A chatbot wrapped with PII / PHI scrubbing at the message-content
boundary. As of v0.2 the profile field routes to a curated bundle
from @pleach/compliance/scrubbers and applies it (plus any
extraScrubbers) to the user message string before ask() forwards
it to the runtime.
import { compliantChatbot } from "@pleach/recipes/compliance";
const bot = compliantChatbot({ profile: "hipaa" });
await bot.ask("patient called about prescription refill");Profile → scrubber bundles:
hipaa→SsnUsScrubber+UsDriverLicenseScrubber+CreditCardScrubbergdpr→SsnUsScrubber+CreditCardScrubberpci-dss→CreditCardScrubbersoc2→SsnUsScrubber+CreditCardScrubber
Scope-limit (v0.2). Scrubbing is applied at the message-content
boundary, NOT at the durable EventLogWriter boundary (next
iteration). Assistant responses are not currently scrubbed.
EventLogWriter-boundary scrubbing is substrate-ready —
@pleach/core's EventLogWriter already accepts a
scrubbers: readonly Scrubber[] constructor option with a
DefaultScrubberChain enforced at every write(), and the
host.modules.eventLogWriter parameter on createPleachRuntime
plumbs a custom writer through. Consumers needing full hash-chain
attestation + attestRun today should construct ComplianceRuntime
directly from @pleach/compliance.
Reach for this when HIPAA, GDPR, PCI-DSS, or SOC 2 requires the event log persisted or exported to be free of unredacted PII.
instrumentedCodingAgent
A coding agent whose executeStep(...) calls automatically
inherit a @pleach/observe sub-agent attribution path. Composes
@pleach/coding-agent, @pleach/sandbox, and @pleach/observe.
import { init } from "@pleach/observe";
import { memory } from "@pleach/observe/destinations";
import { createInMemorySandboxProvider } from "@pleach/sandbox/testing";
import { instrumentedCodingAgent } from "@pleach/recipes/coding-agent";
const dest = memory();
init({ destination: dest });
const agent = instrumentedCodingAgent({
sandboxProvider: createInMemorySandboxProvider({ execHandlers: new Map() }),
subagent: "coding-agent",
});
await agent.start();
try {
const result = await agent.executeStep({
userMessage: "fix the failing test in lib/parser.ts",
stepId: "step-1",
});
console.log(result.ok, dest.rows); // rows tagged subagent: "coding-agent"
} finally {
await agent.stop();
}Compose with outer scopes for nested attribution:
import { subagent } from "@pleach/observe";
await subagent("tenant-abc").run(async () => {
await agent.executeStep({...}); // path: ["tenant-abc", "coding-agent"]
});init({ destination }) from @pleach/observe must be called
once at process start before any executeStep; the SDK is
singleton-per-process.
Reach for this when each turn decomposes into
planner → executor → critic sub-roles and every LLM call needs
to surface on the dashboard tagged by the sub-role that issued
it.
Composing recipes by hand
Every recipe returns a Chatbot with a runtime escape hatch:
interface Chatbot {
readonly runtime: SessionRuntime;
ask(message: string): Promise<string>;
newSession(): Promise<void>;
reset(): Promise<void>;
}When you need a shape the recipe doesn't cover — multi-session
state, streaming to a UI, custom interrupt handling — drop down
to bot.runtime and use executeMessage
directly. Recipes are convenience over @pleach/core; nothing
they wrap is hidden.
@pleach/langchain (preview)
Pre-1.0 adapter bridging LangChain tools, agents, and message history into the Pleach event log. Surface still moving; pin a tight version range.
Reference apps
Two runnable examples that ship inside the @pleach/core npm package — minimal-agent for first-turn baselines, host-adapter for legacy-orchestrator integrations.